Security headers are your cheapest, most effective browser-level defense. Enforced correctly at the CDN edge, they mitigate clickjacking, XSS, MIME-sniffing, credential leaks, and cross-origin isolation attacks without touching application code. This checklist covers the 10 headers you should enforce in production at the CDN layer, with recommended values, edge implementation patterns, and validation steps.
Why Enforce at the CDN Edge?
Most teams set headers in Nginx or application middleware. That works until the CDN serves a cached object that was stored without those headers, or until an origin node is misconfigured. The CDN is the only layer that sees every client response. If you understand what a CDN is, you know the edge terminates TLS, handles caching, and is the ideal policy enforcement point.
Edge enforcement gives you three advantages: consistency (headers attached on HIT and MISS), agility (change policy in seconds without redeploying the origin), and observability (log and report violations centrally). Pair this with a solid CDN SSL/TLS configuration so HSTS and other transport guarantees are not undermined by weak ciphers or mixed content. For dynamic workloads, edge computing lets you generate per-request nonces for CSP without round-tripping to origin.
Rule of thumb: origin may set headers, but the CDN policy overrides. Never rely on Vary for security headers and never strip them at the edge for performance.
The Checklist: 10 Headers for Production
Apply these to all HTML and authenticated responses. For static assets (images, fonts, JS/CSS), you still need a subset — especially CORP, X-Content-Type-Options, and Cache-Control.
| Metric | Edge Processing | Origin Processing | Improvement |
|---|---|---|---|
| Latency (p50) | 8–15 ms | 120–300 ms | 15–20x faster |
| Cache Hit Ratio | 85–95% | N/A | Reduces origin load |
| TLS Handshake | 1–5 ms (nearby PoP) | 50–150 ms | 10–30x faster |
| Bandwidth Cost | $0.01–0.04/GB | $0.05–0.12/GB | 3–5x cheaper |
| Availability | 99.99% (multi-PoP) | 99.9% (single DC) | 10x fewer outages |
| Header | Recommended Production Value | Purpose | Priority |
|---|---|---|---|
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload |
Force HTTPS, prevent SSL stripping | Critical |
| Content-Security-Policy | default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' |
Mitigate XSS, data injection | Critical |
| X-Frame-Options | DENY or SAMEORIGIN |
Block clickjacking (legacy fallback) | High |
| X-Content-Type-Options | nosniff |
Prevent MIME sniffing | High |
| Referrer-Policy | strict-origin-when-cross-origin |
Limit referrer data leakage | High |
| Permissions-Policy | camera=(), microphone=(), geolocation=(), interest-cohort=() |
Disable sensitive browser features | Medium |
| Cross-Origin-Opener-Policy | same-origin |
Isolate browsing context, prevent XS-Leaks | High |
| Cross-Origin-Embedder-Policy | require-corp |
Require CORP/CORS for cross-origin resources | Medium |
| Cross-Origin-Resource-Policy | same-site or same-origin |
Control who can embed your resources | Medium |
| Cache-Control (security-hardened) | no-store for sensitive HTML; public, max-age=31536000, immutable for versioned assets |
Prevent sensitive data caching | Critical |
1. Strict-Transport-Security (HSTS)
Enforce HTTPS for the entire domain. Use a long max-age (2 years) with includeSubDomains and submit to the preload list. Only send HSTS over HTTPS, never over HTTP. At the CDN, add it on all HTTPS responses and ensure HTTP → HTTPS redirects happen at the edge before any content is served. This complements your CDN security hardening and TLS settings.
2. Content-Security-Policy (CSP)
CSP is the most powerful but most fragile header. Start strict: default-src 'self', block object-src, restrict base-uri and frame-ancestors. Avoid unsafe-inline entirely — use nonces or hashes generated at the edge. For third-party scripts (analytics, payments), explicitly allowlist domains and require SRI. Use report-to / report-uri to collect violations before blocking.
3. X-Frame-Options
Legacy but still valuable. Set DENY by default; use SAMEORIGIN only if you intentionally frame your own pages. Keep it alongside frame-ancestors 'none' in CSP for defense-in-depth. CDNs should strip conflicting values from origin and normalize to one.
4. X-Content-Type-Options: nosniff
Stops browsers from MIME-sniffing responses away from the declared Content-Type. Critical for preventing drive-by XSS via uploaded files. Apply to all responses — HTML, JS, CSS, JSON, and especially user-uploaded assets served via CDN.
5. Referrer-Policy
Controls how much referrer information leaks on navigation. strict-origin-when-cross-origin balances privacy and analytics: same-origin sends full URL, cross-origin sends only origin, and downgrade to HTTP sends nothing. Avoid no-referrer-when-downgrade if you handle sensitive query params.
6. Permissions-Policy
Formerly Feature-Policy. Explicitly disable features you don't use: camera, microphone, geolocation, payment, USB, and interest-cohort=() to opt out of FLoC/Topics. This limits damage if an attacker injects an iframe. Keep the list minimal and audit it quarterly.
7. Cross-Origin-Opener-Policy (COOP)
same-origin ensures a top-level page does not share a browsing context group with cross-origin documents, mitigating XS-Leaks and Spectre-style attacks. Required for crossOriginIsolated if you use SharedArrayBuffer. Pair with COEP.
8. Cross-Origin-Embedder-Policy (COEP)
require-corp forces cross-origin resources to explicitly opt in via CORP or CORS. Combined with COOP, it enables cross-origin isolation. Roll out carefully — third-party images/scripts without CORP will be blocked. Use credentialless as a stepping stone if needed.
9. Cross-Origin-Resource-Policy (CORP)
Set on the resource itself to declare who may load it. For most sites, same-site is the sweet spot; for sensitive assets (user avatars, PDFs), use same-origin. cross-origin is only for truly public CDN assets like open-source fonts.
10. Cache-Control (Security-Focused)
Caching and security intersect. For authenticated HTML, API responses, and account pages: Cache-Control: no-store, private plus Pragma: no-cache and Clear-Site-Data on logout. For immutable versioned assets (e.g., app.abc123.js): public, max-age=31536000, immutable. Never cache sensitive HTML at the edge without private or no-store.
Production Implementation at the Edge
Configure headers in your CDN's response policy, not just in origin Nginx. This ensures HITs are protected. Below are minimal, production-ready examples.
Nginx (origin fallback) + CDN override
# /etc/nginx/conf.d/security-headers.conf add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Resource-Policy "same-site" always; # CSP with nonce placeholder - replaced at CDN edge add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$csp_nonce'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
CloudFront Response Headers Policy (AWS)
// CloudFront -> Policies -> Response headers -> Create { "Name": "SecurityHeaders-Prod", "SecurityHeadersConfig": { "StrictTransportSecurity": {"Override": true, "IncludeSubdomains": true, "Preload": true, "AccessControlMaxAgeSec": 63072000}, "ContentTypeOptions": {"Override": true}, "FrameOptions": {"Override": true, "FrameOption": "DENY"}, "ReferrerPolicy": {"Override": true, "ReferrerPolicy": "strict-origin-when-cross-origin"}, "XSSProtection": {"Override": false} // Deprecated - rely on CSP }, "CustomHeadersConfig": { "Items": [ {"Header": "Permissions-Policy", "Value": "camera=(), microphone=(), geolocation=()", "Override": true}, {"Header": "Cross-Origin-Opener-Policy", "Value": "same-origin", "Override": true}, {"Header": "Cross-Origin-Embedder-Policy", "Value": "require-corp", "Override": true} ] } }
Cloudflare Workers / Fastly VCL (per-request nonce)
addEventListener('fetch', event => {
const nonce = btoa(crypto.getRandomValues(new Uint8Array(16)));
let res = await fetch(event.request);
res = new Response(res.body, res);
res.headers.set('Content-Security-Policy',
`default-src 'self'; script-src 'self' 'nonce-${nonce}'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'`);
res.headers.set('X-Content-Type-Options', 'nosniff');
return res;
});Do: Override at Edge
Set Override: true so duplicate origin headers don't create conflicting values. One canonical value per header.
Don't: Use X-XSS-Protection
Deprecated and can introduce new XSS vectors. Modern browsers ignore it. Invest in CSP instead.
Validation, Monitoring & Rollout
Headers are invisible until they break something. Validate continuously:
- Curl the edge, not just origin:
curl -sI https://example.com | grep -i ^strict\|^content-securityand compare HIT vs MISS (curl -H "Pragma: no-cache"). - Scan with automation: Integrate
securityheaders.comAPI, Mozilla Observatory, orcurl+jqin CI. Fail builds if HSTS or CSP is missing. - Report-only first: Deploy
Content-Security-Policy-Report-Onlyfor 7–14 days, collect reports toreport-uri, then enforce. Monitor for blocked third-party widgets. - Log at CDN: Enable CDN logging for
report-toendpoints. Alert on spikes in CSP violations — they often signal an injection attempt or a broken deploy.
Review headers quarterly alongside your CDN fundamentals audit. When you add a new subdomain or third-party script, update CSP and CORP policies before the code ships.
Frequently Asked Questions
Should security headers be set at the origin or CDN edge?
Set them at the CDN edge as the authoritative layer and keep origin headers as fallback. Edge enforcement guarantees consistent headers even for cached assets and shields origin misconfigurations. Use CDN response header policies to override or append.
Will security headers break caching?
No, if you separate concerns. Use Cache-Control for caching and security headers for browser policy. Avoid adding Vary for security headers. Cache security headers with the response at the edge; they do not affect cache keys unless you misconfigure Vary.
Do I still need X-Frame-Options if I have CSP frame-ancestors?
Yes for defense-in-depth. CSP frame-ancestors is the modern standard, but X-Frame-Options is still respected by older browsers and some enterprise proxies. Send both: X-Frame-Options: DENY or SAMEORIGIN and CSP frame-ancestors 'none' or 'self'.
How do I safely roll out a strict CSP without breaking production?
Start with Content-Security-Policy-Report-Only, collect violation reports, allowlist required sources, remove 'unsafe-inline' via nonces or hashes, then enforce. Use the CDN to inject nonces per request and monitor report-uri/report-to endpoints.