Understand what CSP can and cannot protect
Content Security Policy is an HTTP response header that tells a browser where scripts, styles, images, connections, frames, and other resources may come from. If an attacker injects a script tag pointing at an unapproved origin, a well-designed policy can stop the browser from loading it. CSP is defense in depth: it complements output encoding, safe DOM APIs, dependency hygiene, authentication, and server-side authorization; it does not replace them.
Reduce script exposure
Restrict executable JavaScript to approved sources and deliberate inline code.
Constrain data paths
Limit API, analytics, image, and frame connections to known endpoints.
Expose regressions
Violation reports reveal forgotten third parties and unsafe implementation patterns.
A policy is most useful when it is specific. A broad script-src *, unsafe-inline, or permissive wildcard can undermine the control you intended to add. Use the smallest exception that keeps a documented requirement working, then revisit it as the application changes.
Inventory resources before writing directives
Start from real user journeys, not a list of guesses. Sign in, complete a checkout or lead form, load an authenticated page, open embedded media, and test a failure state. Record the origins used for JavaScript, CSS, fonts, images, API calls, WebSockets, analytics, payments, and framed content. Include previews and production because their domains often differ.
Give each external service an owner and a reason to exist. Third-party tags are a frequent source of unexpected script and connection requirements. This inventory pairs naturally with the server-versus-client boundaries in our Next.js environment variables and secret-management guide: browser code should receive only the public configuration it needs.
Begin with Content-Security-Policy-Report-Only
Use Content-Security-Policy-Report-Only first. Browsers will report policy violations but will not block the resource, giving the team evidence about what a real policy would disrupt. Test representative flows, inspect the browser console and reporting endpoint, and distinguish legitimate integrations from accidental or risky behavior before adding an exception.
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://trusted-analytics.example;
style-src 'self';
img-src 'self' data: https://images.example;
connect-src 'self' https://api.example;
object-src 'none';
base-uri 'self';
frame-ancestors 'none'Do not copy this example blindly. A policy must match your own domains and product behavior. Group violations by directive and page, then verify whether each request is expected. When a violation is legitimate, prefer an explicit host over a scheme wildcard; when it is unexpected, trace the code path before allowing it.
Build a focused baseline policy
default-src 'self' is a useful starting point, but important directives deserve explicit choices. Set object-src 'none' unless your product truly needs legacy plugins. Set base-uri 'self' to prevent injected base URLs. Use frame-ancestors to control who may embed your pages, and restrict form-action when forms should submit only to your own application or payment provider.
- script-src: allow the application and named script providers; avoid
unsafe-eval. - style-src: restrict style origins; remove
unsafe-inlineonly after accounting for framework and library behavior. - connect-src: list API, analytics, and WebSocket endpoints used by the browser.
- img-src and font-src: allow the asset hosts your pages actually use, including
data:only when necessary. - frame-src: separate content your app embeds from
frame-ancestors, which controls who embeds you.
Nonces and hashes let you authorize a specific inline script or style instead of reopening all inline execution. They require careful response handling and testing, but are safer than a permanent broad exception. For a Pages Router application, also verify any inline code emitted by libraries or document customization before enforcing a strict policy.
Apply headers deliberately in Next.js
For a policy that is the same on every route, define security headers in next.config.js. Keep the policy string readable and test it in a preview deployment. If a nonce must vary per request, generate it at the edge or server boundary and make sure the rendered markup receives the same nonce; a static header cannot safely provide a per-response value.
// next.config.js
const csp = [
"default-src 'self'",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"img-src 'self' data: https:",
"connect-src 'self' https://api.example",
].join("; ");
module.exports = {
async headers() {
return [{
source: "/(.*)",
headers: [{ key: "Content-Security-Policy-Report-Only", value: csp }],
}];
},
};Header changes are releases. Put the policy behind the same controlled rollout discipline you use for high-risk changes, and make rollback quick. Our Next.js feature-flag guide explains how to define guardrails and ownership before wider exposure.
Operate CSP as a living security control
Once enforcement is on, monitor violations and review changes to the allowlist in code review. Alert on sudden bursts, especially new script-src violations on sensitive routes. A small number of known browser-extension violations can be filtered, but avoid normalizing genuine application violations away. After a vendor or tag is removed, delete its source from the policy.
Include CSP in incident and release checklists. Test authentication, payment redirects, uploads, embedded tools, and error pages after policy updates. When a policy blocks a legitimate request, make the narrowest correction and add a regression test or synthetic check for the affected journey.
Next.js CSP checklist
✓ Inventory real application journeys and external origins
✓ Start with report-only before enforcement
✓ Use explicit directives instead of broad wildcards
✓ Set object-src and base-uri deliberately
✓ Review inline scripts before adding unsafe exceptions
✓ Use nonces or hashes when strict inline control is needed
✓ Monitor violations and assign allowlist ownership
✓ Re-test critical flows after every policy change
Build a safer Next.js application without slowing delivery
Endurance Softwares helps teams ship secure, maintainable Next.js products with practical controls that fit real release workflows.