The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Deny undeclared resource typesContent-Security-Policy: default-src 'none'; base-uri 'none'; object-src 'none'View examples
Allow required same-origin resourcesContent-Security-Policy: default-src 'none'; img-src 'self'; style-src 'self'; connect-src 'self'View examples
Authorize a script with a nonce<script nonce="RANDOM_BASE64_VALUE" src="/assets/app.js"> </script>View examples
Require a script nonceContent-Security-Policy: script-src 'nonce-RANDOM_BASE64_VALUE'; object-src 'none'; base-uri 'none'View examples
Propagate trust from a nonceContent-Security-Policy: script-src 'nonce-RANDOM_BASE64_VALUE' 'strict-dynamic'; base-uri 'none'View examples
Block hostile framingContent-Security-Policy: frame-ancestors 'none'View examples
Restrict form submissionsContent-Security-Policy: form-action 'self' https://payments.exampleView examples
Monitor a candidate policyContent-Security-Policy-Report-Only: default-src 'self'; report-to cspView examples
Name a reporting endpointReporting-Endpoints: csp="https://example.com/reports/csp"View examples
Verify an external script<script src="https://cdn.example/app.js" integrity="sha384-BASE64_DIGEST" crossorigin="anonymous"> </script>View examples
Verify an external stylesheet<link rel="stylesheet" href="/assets/site.css" integrity="sha384-BASE64_DIGEST">View examples
Require Trusted Types at sinksContent-Security-Policy: require-trusted-types-for 'script'; trusted-types app-htmlView examples
Create a TrustedHTML policyconst policy = trustedTypes.createPolicy('app-html', { createHTML: input => sanitize(input) })View examples
Insert untrusted text safelyoutput.textContent = untrustedTextView examples
Validate a link destinationconst url = new URL(candidate, location.origin)View examples
Observe local CSP violationsaddEventListener('securitypolicyviolation', event => console.warn(event.effectiveDirective))View examples

Browser security controls are defense layers, not substitutes for safe application design. Start by eliminating injection paths and treating every external byte as untrusted. Then deliver a measured CSP from the server, pin immutable third-party scripts and styles where Subresource Integrity fits the release process, and migrate dangerous DOM sinks behind narrowly reviewed Trusted Types policies. Roll out reporting carefully, test every route, and remember that CSP restricts resource use but neither authenticates users nor grants cross-origin access.

Step by step

Detailed examples

01

Build a policy from a deny-by-default baseline

Deliver CSP with the HTTP response header so it can govern the whole response and use directives unavailable to meta-delivered policies. default-src is a fallback for many fetch directives, not a complete policy: base-uri, form-action, frame-ancestors, and sandbox need explicit treatment. Inventory real requests per route, add only the origins and schemes that are necessary, and separate resource types instead of granting a broad host everywhere. CSP is an additional mitigation for injection and data exfiltration; it does not sanitize markup, validate URLs, implement CORS, or repair authorization flaws. Multiple enforced policies only add restrictions, which is useful during staged migrations but can be confusing during debugging.

A restrictive same-origin application policy
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self' https://api.example.com; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'
Back to quick reference ↑
02

Authorize executable code with response-specific nonces or hashes

A nonce must be generated with a cryptographically secure source for every HTTP response, placed in both the script-src source expression and the intended script elements, and never derived from request data or reused as a standing secret. Do not expose the nonce through an interpolation point an attacker controls. Hash sources suit byte-stable inline scripts; external scripts can also participate under CSP3 integrity matching, but SRI remains the clearer deployment mechanism for pinned external resources. strict-dynamic transfers trust from a nonce- or hash-authorized script to non-parser-inserted scripts it loads; audit every dynamic script URL because attacker-controlled loader input defeats that trust model. Avoid unsafe-inline and unsafe-eval unless an isolated legacy migration has a documented removal plan.

Send and consume one freshly generated nonce
Content-Security-Policy: script-src 'nonce-4fJ8qV0mP7u2zA6x' 'strict-dynamic'; object-src 'none'; base-uri 'none'

<script nonce="4fJ8qV0mP7u2zA6x" src="/assets/bootstrap.js"></script>

Note: The value is illustrative. Generate a new unpredictable value for every production response and apply the same value to that response's approved scripts.

Back to quick reference ↑
03

Protect embedding and submissions, then stage policies with reports

frame-ancestors constrains which ancestor documents may embed a response and must be sent in an HTTP header; it is ignored in a CSP meta element and does not fall back to default-src. form-action restricts form navigation destinations. Start new rules in Content-Security-Policy-Report-Only, gather reports over representative traffic, remove injected extensions and scanners from the signal, then enforce only after expected paths pass. Report-only is unavailable in meta elements. Treat reports as attacker-controlled telemetry: authenticate transport, cap body sizes and rates, avoid echoing values, and apply retention limits because blocked URLs and samples may contain sensitive data. Reporting is evidence, not proof that every browser enforced the rule.

Monitor a candidate and keep critical navigation rules enforced
Reporting-Endpoints: csp="https://example.com/reports/csp"
Content-Security-Policy: frame-ancestors 'none'; form-action 'self'; base-uri 'none'
Content-Security-Policy-Report-Only: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; report-to csp
Back to quick reference ↑
04

Pin immutable external scripts and styles with SRI

SRI integrity metadata contains one or more algorithm-prefixed base64 digests over the exact fetched representation. Browsers apply supported strongest metadata and block the resource when no eligible digest matches. Use SHA-384 or SHA-512 from a trusted build step, update the digest intentionally whenever bytes change, and prefer immutable versioned URLs so upstream mutation cannot break production unexpectedly. Cross-origin SRI additionally requires a CORS-enabled response; crossorigin=anonymous selects the appropriate request mode but the remote server still must return a permitting Access-Control-Allow-Origin header. SRI verifies bytes, not publisher intent, runtime behavior, or transitive network requests, and current standard HTML integration is for script and stylesheet link resources.

Pin a versioned cross-origin dependency
<script
  src="https://cdn.example/library/4.2.1/library.min.js"
  integrity="sha384-uU7jV0mBK9G2YKz9Jq5n8q2jKv4Tn8oJvH6K1pV5x2yR0eG4dS3L9cW7aF6bN8mQ"
  crossorigin="anonymous"></script>

Note: The digest is structurally illustrative, not a digest for a real file. Generate it from the exact release artifact in the trusted build pipeline.

Generate SHA-384 integrity metadata during a trusted build
openssl dgst -sha384 -binary public/assets/library.min.js | openssl base64 -A
Back to quick reference ↑
05

Migrate DOM XSS sinks behind narrowly scoped Trusted Types policies

Trusted Types enforcement makes covered injection sinks reject ordinary strings and accept the corresponding TrustedHTML, TrustedScript, or TrustedScriptURL value. It is a mitigation for DOM-based injection, not a sanitizer. First remove unnecessary sinks, replace markup insertion with textContent and DOM construction, then place the few unavoidable conversions in small named policies whose create functions perform context-appropriate validation or sanitization. Limit policy creation with the trusted-types directive and introduce require-trusted-types-for 'script' in report-only mode before enforcing. A permissive default policy can hide unsafe call sites and should be a short migration bridge, not the destination. Support is not uniform across browsers, so secure behavior must remain safe without enforcement.

Enforce one reviewed HTML policy
const htmlPolicy = trustedTypes.createPolicy('app-html', {
  createHTML(input) {
    return sanitizer.sanitize(input, { allowElements: ['strong', 'em', 'a'] });
  }
});

const preview = document.querySelector('#preview');
preview.innerHTML = htmlPolicy.createHTML(untrustedMarkup);

Note: sanitizer represents a separately reviewed sanitizer configured for this context; Trusted Types does not perform sanitization itself.

Back to quick reference ↑
06

Prefer APIs that never interpret untrusted data as executable markup

The strongest sink policy is to avoid dangerous sinks. Use textContent for text, createElement and append for structure, setAttribute only for attributes whose grammar you validate, and event listeners instead of inline handler strings. URL parsing alone does not make a destination safe: enforce an allowlist of protocols and, where required, origins before assigning href, src, or a navigation target. Do not pass untrusted strings to innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, Function, or string-taking timer APIs. HTML escaping is context-specific and cannot replace DOM APIs or a mature sanitizer for intentionally accepted markup.

Build a link without an HTML injection sink
function safeProfileLink(label, candidate) {
  const url = new URL(candidate, location.origin);
  if (url.origin !== location.origin || url.protocol !== 'https:') {
    throw new TypeError('Profile URL must be same-origin HTTPS');
  }

  const link = document.createElement('a');
  link.textContent = label;
  link.href = url.href;
  return link;
}
Back to quick reference ↑
07

Test policies as production code and keep deployment recoverable

Exercise CSP in integration tests across authentication, error, payment, localization, and lazy-loaded routes; a home-page smoke test misses most dependencies. Watch the browser console and securitypolicyviolation events locally, but rely on protected server collection for fleet telemetry. Test modern and older supported engines because directive support and fallback behavior differ. Keep a rapid rollback path that restores the last known restrictive policy instead of disabling CSP entirely. Pin header snapshots, nonce uniqueness, SRI generation, and policy-name allowlists in the delivery pipeline, and re-audit after adding analytics, tag managers, embedded content, or framework upgrades.

Capture actionable violation fields during development
addEventListener('securitypolicyviolation', event => {
  console.table({
    directive: event.effectiveDirective,
    blocked: event.blockedURI,
    source: event.sourceFile,
    line: event.lineNumber,
    disposition: event.disposition
  });
});
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumContent Security Policy Level 3w3.org
  2. World Wide Web ConsortiumSubresource Integrityw3.org
  3. World Wide Web ConsortiumTrusted Typesw3.org
  4. MDN Web DocsContent Security Policydeveloper.mozilla.org
  5. MDN Web DocsTrusted Types APIdeveloper.mozilla.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback