Content Security Policy for Third-Party Scripts: From Report-Only to Enforcement

By CellWall Security Research | Published September 17, 2026 | Client-Side Security | 14 min read

Content Security Policy for Third-Party Scripts: From Report-Only to Enforcement
divider
The short answer

Content Security Policy (CSP) is a browser-enforced set of rules delivered in an HTTP response header. For third-party JavaScript, CSP can restrict which scripts execute, where browser connections go, where forms submit, and which pages may frame your site. A safe rollout inventories current behavior, starts in Report-Only, removes unsafe patterns, tests representative journeys, and enforces progressively.

CSP is powerful because enforcement happens where client-side code runs: in the browser. It can make injected scripts, unauthorized origins, unsafe inline handlers, and unexpected connections harder to use. It can also break checkout, authentication, analytics, support, and consent flows when deployed from an incomplete picture of the site.

This guide treats CSP as an operating process rather than a header copied from a generator. The goal is a policy that protects real users, preserves required third-party functionality, and stays aligned as the browser supply chain changes.

What CSP Can—and Cannot—Do

CSP can helpCSP does not prove

Restrict the origins or trusted script entry points from which JavaScript may execute

That code from an allowed origin is safe, necessary, or unchanged

Reject many inline scripts, event handlers, javascript: URLs, and string-to-code patterns

That the application contains no XSS or unsafe DOM data flow

Limit fetch, XHR, WebSocket, beacon, and other connections with connect-src

That every permitted destination receives appropriate data

Restrict form destinations and framing relationships

That business logic, authorization, and user intent are correct

Generate violation telemetry before or during enforcement

That every report is an attack or that unreported behavior is safe

CSP is defense in depth

CSP can reduce exploitability, but it is not a substitute for safe DOM APIs, contextual output encoding, sanitization, dependency management, script inventory, integrity controls, runtime visibility, or incident response. An allowed third-party script generally retains the page privileges the browser gives it.

The Directives That Matter Most

DirectiveWhat it governsThird-party security question

script-src

JavaScript sources and execution rules

Which scripts or trusted entry points may execute?

script-src-elem

Script elements and script blocks

Do script elements need a narrower policy than the general script fallback?

script-src-attr

Inline event handlers

Can handlers such as onclick be removed instead of allowing inline execution?

connect-src

Fetch, XHR, WebSocket, EventSource, and beacon destinations

Where may page code transmit or retrieve data?

frame-src

Frames the protected page may load

Which embedded providers are required?

frame-ancestors

Who may embed the protected page

Should another origin be allowed to frame this page?

form-action

Where forms may submit

Could an injected or modified form send sensitive data elsewhere?

object-src

Plugin-based embedded content

Can legacy object execution be disabled with 'none'?

base-uri

Permitted targets for the document's <base> element

Can an injected base URL rewrite relative script or link destinations?

default-src

Fallback for many fetch directives that are not explicitly set

Which resource types still inherit a broad default?

report-to / report-uri

Violation report destinations

Where are reports collected, normalized, and reviewed?

`default-src` is not a universal fallback

Directives such as frame-ancestors, form-action, and base-uri need their own decisions. Do not assume default-src 'none' automatically provides every navigation, framing, and document restriction you intend.

Inventory Before You Write the Policy

A policy based only on page source or a tag-manager export will miss conditional resources and nested dependencies. Begin with a third-party JavaScript inventory built from representative browser sessions, then connect each observed resource and destination to an owner, purpose, loading chain, approved scope, and removal path.

Observe representative states

  • Critical journeys such as authentication, account recovery, checkout, payment, and support.

  • Anonymous and authenticated sessions, consent choices, locales, devices, experiments, and error paths.

  • Scripts introduced by application bundles, tag managers, consent tools, frames, widgets, and loaders.

  • Network destinations used for APIs, telemetry, media, fraud services, and vendor dependencies.

Decide before allowing

  • Confirm the internal owner and specific purpose of each integration.

  • Record where the script is approved to run and which destinations it requires.

  • Remove obsolete resources and narrow wildcard or shared-host dependencies where practical.

  • Identify code patterns that require refactoring: inline handlers, eval(), injected script URLs, or broad dynamic loaders.

Allowlist CSP vs. Strict CSP

ApproachTrust decisionStrengthOperational tradeoff

Host allowlist

Trust scripts from named origins

Can block unlisted origins and is often easier to introduce

Large or shared origins can authorize more code than intended; vendor domain changes expand maintenance

Nonce-based strict CSP

Trust script elements carrying a fresh unpredictable response-specific nonce

Avoids relying primarily on long host lists and works well with dynamic server-rendered HTML

Requires per-response nonce generation and deliberate propagation to trusted script elements

Hash-based strict CSP

Trust specific inline or external script bytes represented by hashes

Fits static HTML and binds trust to content

Every content change requires a new hash; dynamic scripts can be difficult

Strict CSP with strict-dynamic

Allow a nonce- or hash-trusted script to load further scripts

Can support legitimate loaders without enumerating every descendant origin

Delegates trust down the loading chain; unsafe loader behavior remains dangerous

A strict CSP is the preferred target for mitigating script injection when the architecture can support it. A carefully scoped allowlist can still provide value, especially during migration, but adding domains until reports disappear often produces a policy that looks complete while permitting broad execution paths.

A Practical Report-Only Starting Point

Report-Only evaluates a proposed policy without blocking violations. It is useful for discovering incompatibilities and testing a future policy, but it does not protect users until an enforcing Content-Security-Policy header is also active.

http
Reporting-Endpoints: csp-endpoint="https://reports.example.com/csp" Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com; img-src 'self' data:; style-src 'self'; font-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; report-to csp-endpoint; report-uri https://reports.example.com/csp
This is a diagnostic baseline, not a universal production policy

Replace the example origins and directives with decisions based on your application. A policy copied without representative testing may miss required resources, authorize unnecessary ones, or create a false sense of protection.

Nonce-Based Strict CSP

For dynamically rendered HTML, generate an unpredictable nonce for every response. Put the same value in the response's script-src directive and only on script elements the application deliberately trusts.

http
Content-Security-Policy: script-src 'nonce-{FRESH_RANDOM_VALUE}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'
html
<script nonce="{FRESH_RANDOM_VALUE}" src="/app.js"></script> <script nonce="{FRESH_RANDOM_VALUE}" src="https://vendor.example/widget.js"></script>
Never reuse or blindly inject nonces

The nonce must be unpredictable and different for every HTTP response. Do not use middleware that adds the nonce to every script tag after rendering: an attacker-injected script could receive the same trust token. Apply it through a trusted rendering path.

Hash-Based CSP for Static Pages

Static HTML cannot generate a fresh nonce per response. A hash-based policy can authorize exact script content instead. Recalculate the CSP hash whenever the authorized script bytes change—even whitespace changes the result.

http
Content-Security-Policy: script-src 'sha256-{BASE64_HASH}' 'strict-dynamic'; object-src 'none'; base-uri 'none'

CSP hashes and Subresource Integrity are related but distinct. CSP decides whether code may execute under the page's policy. SRI verifies that an eligible fetched resource matches expected bytes. For external scripts, browser requirements and markup details differ, so test the combination rather than treating one hash as an interchangeable control everywhere.

Move From Report-Only to Enforcement in Eight Steps

1

Define the protection goal

Decide whether the first milestone targets script injection, exfiltration destinations, framing, form submissions, or a broader strict policy. Name the journeys and owners in scope.

2

Build the observed baseline

Inventory scripts, initiators, frames, connections, and conditional states from representative browser sessions.

3

Choose the policy architecture

Prefer nonce- or hash-based strict CSP where feasible. Document any temporary host allowlist and why it is necessary.

4

Deploy Report-Only

Send the proposed policy as an HTTP response header and collect reports at a controlled endpoint without blocking users.

5

Classify violations

Separate required application behavior, obsolete integrations, policy gaps, extensions or environmental noise, and suspicious activity.

6

Refactor and narrow

Remove inline handlers and unsafe code patterns, minimize third parties, constrain destinations, and avoid broad wildcard exceptions.

7

Enforce progressively

Start with low-risk routes or a traffic cohort, keep a tested rollback path, and verify business and security telemetry before expanding.

8

Operate the policy

Review policy changes like code, test releases and vendor changes, monitor violations and runtime behavior, and retire exceptions that no longer have an owner.

How to Triage CSP Violation Reports

SignalLikely explanationNext action

Known resource on an expected journey

Policy is missing a genuinely required behavior

Confirm owner and purpose, then add the narrowest justified rule or refactor

Known resource on an unexpected page

Scope drift, shared template, tag trigger, or routing behavior

Trace the initiator and correct the loading scope before broadening CSP

Unknown script or destination

Undocumented integration, injected code, compromised dependency, or browser environment noise

Preserve context, correlate across sessions, identify the initiator, and investigate

Inline-script violation

Inline code, event handler, framework behavior, or injection attempt

Use source location and sample carefully; refactor legitimate code to nonces, hashes, or external handlers

Sudden report spike after release

Application or policy deployment changed

Correlate with the release, reproduce critical journeys, and roll back or repair deliberately

Reports isolated to unusual clients

Extension, malware, proxy, automation, or a narrow user state

Cluster by user agent and context; do not globally allow the source just to suppress noise

A blocked URI alone is not enough context. Preserve the effective directive, disposition, document route, source location when available, release version, session conditions, and the resource's initiator chain. Prioritize repeated signals on sensitive journeys over isolated extension noise.

InsightAnalyst
Treat reports as potentially sensitive and attacker-controlled

Validate request methods and content types, authenticate ingestion where practical, apply rate limits, minimize retained fields, and avoid reflecting report values into an unsafe dashboard. CSP telemetry can contain URLs and other context that should follow your data-handling rules.

Third-Party Script Decision Guide

Third-party patternPreferred treatmentResidual risk

Static versioned library

Pin the version, use SRI where compatible, and authorize through a nonce, hash, or narrow source rule

Approved bytes may still contain vulnerabilities or unsafe behavior

Dynamic vendor script at a stable URL

Limit page scope and destinations, document the provider, and monitor content and behavior changes

CSP cannot distinguish a legitimate update from malicious code served by the same allowed source

Tag manager

Restrict publishing access, govern tags and triggers, nonce the trusted loader where appropriate, and observe descendants

With strict-dynamic, a trusted loader may pass trust to scripts it creates

Embedded widget

Follow iframe security best practices: prefer a cross-origin sandboxed frame, narrow Permissions Policy, and validated messaging

Frame permissions and postMessage handling can reintroduce access

Script requiring unsafe-eval

Challenge the requirement, upgrade or replace the dependency, and isolate it when removal is not immediate

unsafe-eval restores dangerous string-to-code execution paths

Broad shared CDN

Prefer a dedicated path, self-hosted reviewed artifact, hash, or narrower provider endpoint

Origin-level allowlisting may authorize unrelated attacker-controlled content on the same service

Common CSP Mistakes

Policies that look safer than they are

  • Adding *, broad schemes, shared cloud origins, or large wildcard domains until reports stop.

  • Using 'unsafe-inline' or 'unsafe-eval' as permanent compatibility switches.

  • Assuming 'self' means the delivered code is trusted, immutable, or free from upload and JSONP-style execution paths.

  • Using CSP as the only defense against XSS or third-party compromise.

Rollouts that create operational risk

  • Enforcing from a homepage-only scan without testing authenticated and transactional journeys.

  • Copying a generic policy without assigning owners to exceptions and breakage decisions.

  • Automatically allowlisting every reported source, including browser-extension or malware noise.

  • Deploying once and never comparing the policy with releases, tag changes, and runtime drift.

Pre-Enforcement Checklist

Security readiness

  • The policy has an explicit goal and avoids unjustified wildcards and unsafe keywords.

  • Nonce values are unpredictable, response-specific, and applied only by trusted rendering code—or hashes are generated from controlled content.

  • object-src, base-uri, form-action, frame-ancestors, and connection destinations have deliberate values.

  • Every third-party exception maps to an inventory record, owner, purpose, and approved scope.

Operational readiness

  • Representative journeys pass in supported browsers, including consent, payment, login, error, and regional variants.

  • Violation collection is rate-limited, privacy-reviewed, normalized, and connected to an accountable response process.

  • Application releases, tag-manager publishing, and vendor changes trigger policy validation.

  • The rollout has measurable success criteria, a staged expansion plan, and a tested rollback path.

Test the response header the browser actually receives—not only the framework configuration. CDNs, reverse proxies, middleware, redirects, error pages, and cached HTML can produce a different policy or omit it entirely.

InsightDeveloper

Where CSP and SiteWall Fit Together

CSP expresses browser-enforced rules. SiteWall can support the surrounding operating process by helping teams observe resources, providers, loading relationships, destinations, sensitive browser behavior, and changes across captured sessions. That context can make policy design and violation investigation more informed.

The controls are complementary. CSP can block behavior that violates the active policy; third-party JavaScript monitoring can help reveal changes within permitted relationships and browser states. The CSP vs. SRI vs. monitoring comparison maps each layer to the failures it prevents, detects, and misses. Your team remains responsible for policy design, coverage, authorization, testing, incident handling, and decisions about acceptable behavior.

Featured Product

Build CSP From What Browsers Actually Run

Evaluate SiteWall on representative journeys to see how resource discovery, loading relationships, destinations, browser behavior, and change context can support CSP design and ongoing third-party governance.

Explore Product

Frequently Asked Questions

What is Content Security Policy?

Content Security Policy is a browser security mechanism configured mainly through HTTP response headers. It lets a site restrict resource loading and behaviors such as script execution, network connections, form submissions, and framing.

Should CSP start in Report-Only mode?

For an existing application, Report-Only is usually the safest way to evaluate a proposed policy without blocking users. It still requires representative testing and triage, and it provides no blocking protection until an enforcing policy is deployed.

What is the difference between CSP Report-Only and enforcement?

A Content-Security-Policy-Report-Only header monitors violations and can send reports but does not block them. A Content-Security-Policy header enforces its rules. Both can be active simultaneously to enforce one policy while evaluating a stricter future policy.

Are CSP nonces reusable?

No. A nonce should be unpredictable and newly generated for every HTTP response. The server places that response-specific value in the CSP header and only on script or style elements it deliberately trusts.

Does `strict-dynamic` make third-party scripts safe?

No. It lets a nonce- or hash-trusted script pass execution trust to scripts it creates. This can simplify policies for loaders, but it also makes the trusted loader and its script-creation behavior security-critical.

Can CSP stop a compromised allowed third party?

Not necessarily. If malicious code is delivered through a source or trust path your policy allows, CSP may permit it. Layer CSP with SRI where appropriate, inventory, supplier controls, behavior monitoring, data-flow restrictions, and incident response.

Can CSP be delivered in a meta tag?

An enforcing policy can be placed in a meta element, but it must appear early and does not support the full header feature set. Report-Only, frame-ancestors, reporting directives, and some other capabilities require an HTTP response header. Header delivery is preferred.

How often should a CSP be reviewed?

Review it whenever application code, tags, vendors, routes, or browser integrations change, and on a scheduled cadence based on risk. Policy exceptions should have owners and should expire or be revalidated rather than accumulating indefinitely.

A Policy Is a Maintained Boundary

The most effective CSP is not the longest or the strictest-looking header. It is the narrowest policy your architecture can support, backed by a verified inventory, safe code patterns, representative testing, useful reporting, accountable exceptions, and a controlled path from observation to enforcement.

Start with one critical journey. Observe it, write the intended boundary, run that boundary in Report-Only, resolve violations deliberately, and enforce it for a limited cohort or route. Once the feedback loop works, expand coverage without turning every new report into permanent trust.

Read the practical guides that clarify the surrounding risks, controls, and evidence.

Explore the client-side security hub