The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Deny undeclared resource types | Content-Security-Policy: default-src 'none'; base-uri 'none'; object-src 'none' | View examples |
| Allow required same-origin resources | Content-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 nonce | Content-Security-Policy: script-src 'nonce-RANDOM_BASE64_VALUE'; object-src 'none'; base-uri 'none' | View examples |
| Propagate trust from a nonce | Content-Security-Policy: script-src 'nonce-RANDOM_BASE64_VALUE' 'strict-dynamic'; base-uri 'none' | View examples |
| Block hostile framing | Content-Security-Policy: frame-ancestors 'none' | View examples |
| Restrict form submissions | Content-Security-Policy: form-action 'self' https://payments.example | View examples |
| Monitor a candidate policy | Content-Security-Policy-Report-Only: default-src 'self'; report-to csp | View examples |
| Name a reporting endpoint | Reporting-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 sinks | Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-html | View examples |
| Create a TrustedHTML policy | const policy = trustedTypes.createPolicy('app-html', { createHTML: input => sanitize(input) }) | View examples |
| Insert untrusted text safely | output.textContent = untrustedText | View examples |
| Validate a link destination | const url = new URL(candidate, location.origin) | View examples |
| Observe local CSP violations | addEventListener('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
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.
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' 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.
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.
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.
<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.
openssl dgst -sha384 -binary public/assets/library.min.js | openssl base64 -A 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.
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.
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.
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;
} 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.
addEventListener('securitypolicyviolation', event => {
console.table({
directive: event.effectiveDirective,
blocked: event.blockedURI,
source: event.sourceFile,
line: event.lineNumber,
disposition: event.disposition
});
}); Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



