Decloak is an AI-powered web security intelligence platform that scans a site's HTTP/TLS posture, JavaScript, and third-party scripts to produce a report anyone can read. This guide is part of Decloak's library of practical, source-backed security guidance.
In this guide
- Key takeaways
- What is a CSP header and why should I use it?
- How do I deliver the CSP header?
- What is the basic syntax of a CSP policy?
- Which fetch directives should I include?
- How do I block everything by default?
- How can I safely allow inline scripts or styles?
- How do I prevent dangerous eval - style APIs?
- How can I stop my site from being framed (clickjacking protection)?
- How do I upgrade mixed content automatically?
- How do I get feedback on policy violations?
- What are best - practice implementation steps?
- How does CSP complement other security layers?
- Quick example for a typical Node.js Express app
- Summary
Key takeaways
- Deliver the CSP header on every HTTP response.
- Use
default-srcas a fallback and specify fetch directives for scripts, styles, images, etc. - Block inline code with nonces or hashes; avoid
'unsafe - inline'. - Test with
Content - Security - Policy - Report - Onlybefore enforcement. - Add optional directives for framing protection, mixed - content upgrade, and violation reporting.
What is a CSP header and why should I use it?
A Content - Security - Policy (CSP) header tells browsers which resources are allowed to load, dramatically reducing the risk of cross - site scripting (XSS) and data - injection attacks. By enforcing a whitelist, any unexpected script, style, or frame is blocked before it can execute.
How do I deliver the CSP header?
Set the header in every HTTP response, not just the main HTML page. Use the standard header name Content - Security - Policy for enforcement or Content - Security - Policy - Report - Only while testing. Example (NGINX):
add_header Content - Security - Policy "default-src 'self'; script-src 'self' 'nonce-${nonce}';" always;
Replace ${nonce} with a per - request generated value.
What is the basic syntax of a CSP policy?
A CSP is a semicolon - separated list of directives. Each directive consists of a name followed by one or more source expressions. Example:
Content - Security - Policy: default-src 'self'; img-src https: cdn.example.com;
default-src provides a fallback for any resource type that lacks an explicit directive.
Which fetch directives should I include?
Start with a minimal set and expand as needed:
| Directive | Purpose |
|---|---|
default-src | Fallback for all types |
script-src | Allowed JavaScript sources |
style-src | Allowed CSS sources |
img-src | Allowed image origins |
font-src | Allowed web - font origins |
connect-src | Allowed XHR, WebSocket, EventSource |
media-src | Allowed audio/video sources |
object-src | Allowed plugin objects |
frame-ancestors | Origins allowed to embed the page |
How do I block everything by default?
Use 'none' for a directive you want to completely disallow, for example:
object-src 'none';
If you omit a directive, the default-src value applies.
How can I safely allow inline scripts or styles?
Nonces - Generate a random base64 string for each response, add it to the header and to each <script>/<style> tag:
<script nonce="abc123def456">…</script>
Header:
script-src 'nonce-abc123def456';
Only elements with the matching nonce will execute.
Hashes - Compute a SHA - 256/384/512 hash of the inline code and list it in the directive:
script-src 'sha256 - XyZ…';
Hashes are ideal for static pages because the content does not change.
Never use 'unsafe-inline'; it defeats the purpose of CSP.
How do I prevent dangerous eval - style APIs?
By default script-src (or default-src) blocks eval(), new Function(), and setTimeout(string). Only add 'unsafe-eval' if you have a compelling reason, and pair it with a nonce or hash to limit the impact.
How can I stop my site from being framed (clickjacking protection)?
Add the frame-ancestors directive. To block all framing:
Content - Security - Policy: frame-ancestors 'none';
Older browsers also respect X - Frame - Options, but CSP provides a modern, flexible replacement.
How do I upgrade mixed content automatically?
Include the directive upgrade-insecure-requests (no value). Browsers will rewrite any http: URLs to https: before loading them:
Content - Security - Policy: upgrade-insecure-requests;
This helps during a migration to HTTPS.
How do I get feedback on policy violations?
- Deploy a
Content - Security - Policy - Report - Onlyheader to log violations without blocking content. - Add a reporting endpoint using
report-to(preferred) or the deprecatedreport-uridirective:
Content - Security - Policy - Report - Only: default-src 'self'; report-to csp-endpoint;
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Browsers send JSON payloads for each violation, letting you fine - tune the policy.
What are best - practice implementation steps?
- Start with a report - only policy - Use a minimal set of directives and monitor reports.
- Add nonces or hashes - Replace any
'unsafe-inline'usage. - Gradually tighten - Move from
report - onlyto enforcedContent - Security - Policyonce no false positives appear. - Include optional hardening -
frame-ancestors 'none',upgrade-insecure-requests, and areport-toendpoint. - Automate nonce generation - In server - side code (Node, PHP, etc.) generate a fresh nonce per response and inject it into the header and HTML.
How does CSP complement other security layers?
CSP is one part of a defense - in - depth strategy. It works alongside HTTPS/TLS hardening, secure cookie flags, and server - side input validation. While CSP blocks many client - side injection vectors, it does not replace proper backend security.
Quick example for a typical Node.js Express app
const crypto = require('crypto');
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.nonce = nonce; // use in templates
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}'`,
"style-src 'self'",
"img-src 'self' data:" ,
"object-src 'none'",
"frame-ancestors 'none'",
"upgrade-insecure-requests"
].join('; ');
res.setHeader('Content-Security-Policy', csp);
next();
});
In your HTML template:
<script nonce="{{nonce}}">console.log('Hello');</script>
This enforces a strict CSP while allowing only the scripts you explicitly nonce.
Summary
Implementing CSP starts with delivering the header on every response, defining a sensible set of directives, and using nonces or hashes to allow required inline code. Test with Report - Only, add framing and mixed - content protections, and monitor violation reports to refine the policy. A well - crafted CSP dramatically reduces XSS risk and complements other security measures.
Related guides
How to Fix a Missing Content - Security - Policy Header
Learn concrete steps to add, test, and harden a Content - Security - Policy header, from server configuration to iterative reporting and verification.
Is HTTP 1.1 a security risk?
HTTP 1.1 is not a direct vulnerability, but its plain - text design and parsing ambiguities can create attack surfaces that need mitigation, especially when not used over TLS.
Is JavaScript a security risk?
JavaScript can be a major attack surface because injected scripts run with the same privileges as the page, but proper sanitization, CSP, and safe frameworks eliminate most risks.