Build a CSP rollout around nonces and actual application flows

Move from a dependency inventory to report-only observation and enforcement, with concrete nonce and reporting decisions.

A useful Content Security Policy is built around the application's actual execution paths. Copying a short policy into a server configuration can block login, payments or a framework's runtime scripts while leaving important injection paths poorly understood. Start with an inventory, observe violations and enforce a policy whose trust decisions you can explain.

CSP is a defence in depth against certain classes of content injection and resource misuse. It does not replace output encoding, safe DOM APIs or server-side access control. Its value depends on the directives you deploy and the code paths they permit.

Map scripts and resource destinations

List application scripts, inline bootstrap code, styles, images, fonts, API connections, frames and workers. Include authenticated pages, error pages and third-party flows. A homepage-only inventory misses many dependencies that appear after a user signs in or completes a purchase.

The CSP auditor reviews a pasted policy for directives and common concerns. It does not crawl your application or prove that the policy blocks every injection technique. Use it to review the text while browser testing supplies evidence about actual behaviour.

Assign an owner to each allowed external origin. A broad wildcard can be convenient but may authorise more content than the application needs. Ask what the origin supplies and whether user-controlled content can be hosted there before treating the whole host as trusted script infrastructure.

Use nonces as per-response authorisation

A script nonce is an unpredictable value generated for a response and included in both the policy and the authorised script element. It is not a reusable secret stored permanently in the frontend source. Generate it with an appropriate random source and refresh it for each response according to the application's design.

Content-Security-Policy-Report-Only:
  script-src 'nonce-REPLACE_PER_RESPONSE';
  object-src 'none'; base-uri 'none'

<script nonce="REPLACE_PER_RESPONSE" src="/app.js"></script>

The placeholder illustrates the relationship and must not be deployed literally. The response header and authorised element must receive the same generated value. Do not automatically add the nonce to arbitrary script markup supplied by untrusted input, because that would authorise the very content the policy should constrain.

Review caching carefully. If an edge caches an HTML response and its policy, the nonce lifecycle must remain consistent with the security design. A header generated independently from cached HTML can mismatch and block scripts. Reusing a fixed nonce indefinitely also undermines the intended model.

CSP rollout evidence
StageEvidenceDecision
InventoryResources used by representative flowsWhich destinations and execution paths are necessary?
Report-onlyViolations plus browser reproductionWhich failures reflect real application dependencies?
Policy reviewDirective semantics and nonce handlingDoes each allowance have a justified owner?
EnforcementSuccessful critical workflows and blocked test casesCan the policy ship at the intended scope?
MaintenanceNew dependencies and violation trendsDoes a change require policy or application repair?

Use report-only as observation, not protection

Content-Security-Policy-Report-Only lets a browser report violations of a candidate policy without enforcing that candidate. It is useful for discovering dependencies, but it should not be described as blocking those violations. An enforced policy can coexist with a separate report-only candidate.

Configure reporting according to current browser support and your endpoint design. Reports can contain URLs and other context, so handle them as application telemetry with appropriate access and retention. Validate incoming report data and avoid rendering it as trusted HTML in a dashboard.

Expect noise from extensions, injected local software and unusual client environments. Reproduce a reported violation in a controlled browser before adding an origin to the policy. Automatically allowing every reported destination would let untrusted observations expand the application's trust boundary.

Review fallback rules and independent directives

default-src provides fallback behaviour for several resource directives, but it is not a universal substitute for every directive. Read the specific fallback relationships for script, style, connection and worker behaviour. Directives such as base-uri and frame-ancestors need their own deliberate review.

frame-ancestors controls who may embed the document, while frame-src concerns frames the document loads. Confusing these directions can leave a clickjacking requirement unmet or break an embedded application. Test both the parent and child context when your product uses framing.

connect-src affects fetches, WebSockets and other connection types governed by the directive. A page can render correctly while its API calls fail. Include data submission, live updates and error reporting in the acceptance test, not only visual loading.

Understand strict-dynamic before adding it

With appropriate nonce or hash trust, strict-dynamic changes how trust can propagate to scripts loaded by trusted scripts in supporting browsers. It also changes the role of some host-source expressions. Do not add it as a decorative keyword without understanding the script-loading architecture.

Review whether trusted bootstrap code loads scripts from URLs influenced by user input. A trusted loader can become a sensitive part of the policy's security model. CSP cannot repair a loader that grants execution to arbitrary destinations simply because its own script was authorised.

Test supported browser versions and any intended fallback policy. A policy can have different effective behaviour in older implementations. Document that difference rather than assuming the same source list means the same enforcement everywhere.

Repair a checkout violation without broadening everything

Suppose report-only logs show a blocked payment frame and a blocked API connection during checkout. Reproduce the flow with a test account and payment sandbox. Identify the exact frame origin and connection destination, including redirects.

Add only the required destinations to the relevant directives after checking provider documentation and ownership. Do not solve a frame violation by adding unsafe-inline to script-src; that changes a different permission and does not explain the failure. Likewise, an API connection belongs in its connection policy, not an unrelated image allowance.

Repeat success, cancellation, failed payment and authentication challenge paths. Third-party integrations often use different resources for exceptional flows. Enforce the candidate only after these paths work and after a controlled unauthorised resource is demonstrably blocked.

Inspect the final response for multiple policies

Several enforced CSP policies generally apply together, restricting the result according to each policy rather than combining into one permissive union. A proxy-added policy can therefore block resources allowed by an application-added policy. Inspect all response headers at the public edge.

A meta-delivered policy also has limits compared with a response header, including timing and directive support. Prefer a response-header deployment when it fits the application. Do not assume a late meta element governs resources that loaded before the browser encountered it.

Keep one clear configuration owner where possible, with tests for the actual public response. A source-code review that sees only the application policy can miss restrictions injected by hosting middleware or a CDN.

Test a nonce mismatch as a release failure

In a controlled build, deliberately give the header and one required script different nonce values. Confirm that enforcement blocks the script and that monitoring identifies the failed application flow. Restore the matching generated values and repeat. This verifies the actual response path, including caches and templates, instead of only checking that a nonce attribute exists. Keep the mismatch fixture out of production and use it to catch later deployment changes that generate policy and markup independently. The test should also confirm that untrusted markup never receives the valid nonce.

Frequently asked questions

Does report-only stop an injected script?

The report-only policy observes violations without enforcing that candidate. An independently enforced policy may still block the script. Label the two policies clearly and verify the actual browser result rather than interpreting every console warning as a successful block.

Can I reuse one nonce across the whole site?

A permanently reused nonce does not fit the intended unpredictable per-response authorisation model. Generate and inject values through the trusted response path, keeping headers and markup consistent. Review caching and templating so untrusted content cannot acquire an authorised nonce automatically.

Will unsafe-inline fix every broken page?

It can weaken restrictions on inline content where applicable and may not address the actual failing directive. Identify the blocked operation first. Refactor inline code, use appropriate nonces or hashes, and permit required destinations deliberately instead of broadening unrelated permissions.

Are hashes useful for static inline scripts?

They can authorise exact script content under CSP rules. The digest must match the relevant bytes, so formatting changes can require an updated policy. Keep the build process responsible for matching content and hashes, and test the generated response after minification or templating.

Does CSP replace sanitisation?

No. Continue using safe output handling and DOM APIs appropriate to the data. CSP limits certain execution and loading paths, but allowed code and resources can still contain vulnerabilities. Treat it as another layer with explicit assumptions and tests.

Why does a resource fail despite being in my source list?

Check the directive that applies, redirects, nonce requirements and every additional enforced policy. The resource may be governed by a different directive or restricted by another layer. Inspect the complete violation details and public response before changing the list.

What should trigger a policy review after launch?

New script loaders, third-party integrations, framework changes and altered caching can change the trust model. Review those changes with representative flows and current reports. A CSP is maintained alongside the application; a previously valid policy is not proof that a new dependency is appropriate.

Sources and further reading

50 useful tools · ↑ ↓ to navigate · Enter to open · Esc to close