Security Headers
HTTP response security headers: Content-Security-Policy, HSTS, X-Frame-Options, CORS configuration, COOP/COEP isolation, and violation reporting endpoints.
What it is
HTTP security headers are response headers that instruct browsers on security policy for rendering, executing, and sharing content from a web application. They provide defence against cross-site scripting (XSS), clickjacking, MIME-sniffing, cross-origin information leakage, and downgrade attacks — all enforced by the browser before any application code executes. The key headers are: Content-Security-Policy (CSP) — the most powerful, defines approved sources for scripts, styles, images, fonts, and frames; blocks inline scripts by default; prevents XSS by preventing the execution of injected scripts; Strict-Transport-Security (HSTS) — instructs browsers to always use HTTPS for the domain, preventing SSL-stripping attacks; X-Frame-Options / CSP frame-ancestors — prevents clickjacking by disallowing the page from being embedded in an iframe on another origin; X-Content-Type-Options: nosniff — prevents MIME-type confusion attacks where a browser incorrectly interprets a response as executable code; and Referrer-Policy — controls how much URL information is sent in the Referer header, preventing URL-based information leakage (tokens in query parameters).
Newer headers address cross-origin isolation: Cross-Origin-Opener-Policy (COOP) isolates the browsing context group, preventing cross-origin windows from accessing the document's DOM; Cross-Origin-Embedder-Policy (COEP) requires all resources loaded by the page to be CORS-enabled or have CORP headers — together, COOP + COEP enable SharedArrayBuffer and high-resolution timers for applications that need them (e.g., WebAssembly workloads) while preventing Spectre side-channel attacks. Permissions-Policy (formerly Feature-Policy) restricts access to browser APIs (camera, microphone, geolocation, payment) per origin.
Why it exists
Browser security policies are enforced client-side by the browser, providing a second line of defence independent of server-side application logic. CSP, for example, prevents XSS impact even when an attacker successfully injects a malicious script into a page response — the browser refuses to execute it because the script source doesn't match the CSP allowlist. HSTS prevents SSL stripping on networks where an attacker intercepts unencrypted HTTP requests and prevents them from being upgraded to HTTPS. These headers cost almost nothing to implement correctly but provide significant risk reduction against well-understood attack classes.
When to use
- All web applications — the full set of baseline headers (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) should be present on every response.
- Applications handling sensitive data — CSP is particularly important for financial and healthcare applications where XSS could exfiltrate credentials or session tokens.
- Applications using CORS — CORS must be explicitly and precisely configured; the default same-origin policy is secure, but misconfigured CORS headers create cross-origin vulnerabilities.
- Applications using SharedArrayBuffer or WebAssembly threading — COOP + COEP are required by browsers for these features.
When not to use
- Strict CSP with
unsafe-inlineblocked can break applications that rely on inline scripts (e.g., legacy analytics, ad platforms, CMS). CSP should be implemented with a migration plan, not as a breaking change. Use CSP in report-only mode first to identify violations before enforcing.
Typical architecture
RECOMMENDED SECURITY HEADERS (production baseline):
# Force HTTPS; include subdomains; preload list submission
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Prevent MIME-type sniffing
X-Content-Type-Options: nosniff
# Clickjacking protection (modern: use CSP frame-ancestors)
X-Frame-Options: DENY
# Limit referrer information (no full URL for cross-origin requests)
Referrer-Policy: strict-origin-when-cross-origin
# Disable browser features not needed by the app
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
# Content Security Policy (strict example):
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{RANDOM_NONCE}' https://cdn.trusted.com;
style-src 'self' 'nonce-{RANDOM_NONCE}';
img-src 'self' data: https://images.trusted.com;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
report-uri https://csp.example.com/report
# Cross-origin isolation (for SharedArrayBuffer / WASM threads):
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
CORS CONFIGURATION (API):
Access-Control-Allow-Origin: https://app.example.com
# NEVER: Access-Control-Allow-Origin: * with credentials
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true # only with specific origin
Access-Control-Max-Age: 86400 # preflight cache duration
CSP NONCE PATTERN (prevents inline script injection):
Server generates cryptographic nonce per request:
nonce = crypto.randomBytes(16).toString('base64')
Header: script-src 'nonce-{nonce}'
Template: <script nonce="{nonce}">...</script>
Injected scripts have no nonce → blocked by CSP
CSP REPORT-ONLY MODE (safe rollout):
Content-Security-Policy-Report-Only: [policy]; report-uri /csp-report
Violations reported but not blocked — use for 2-4 week trial
Pros and cons
Pros
- CSP eliminates impact of many XSS vulnerabilities: even if an attacker injects a script, it cannot execute without a matching nonce or hash.
- HSTS with preload eliminates SSL-stripping attacks entirely — browsers refuse to make plain HTTP connections to preloaded domains.
- Headers are set at the infrastructure layer (CDN, load balancer, reverse proxy) in minutes and apply to all responses without code changes.
- CSP violation reporting provides real-time visibility into attempted XSS attacks and misconfigured resource loading.
Cons
- Strict CSP breaks applications that rely on inline scripts, eval(), or third-party scripts without CSP-compatible loading patterns.
- CSP with nonces requires server-side templating for each request; static site generators and CDN-cached responses cannot use per-request nonces.
- CORS misconfiguration (overly permissive) is easy to make and creates cross-origin vulnerabilities; correct configuration requires understanding of the same-origin policy.
- HSTS preload is a one-way commitment; once a domain is preloaded, removing HTTPS support requires a multi-year wait for browser updates.
Implementation notes
Set security headers at the infrastructure layer (CDN, load balancer, or reverse proxy) rather than in application code, so they apply universally to all endpoints including those from third-party components. Use a CSP grade checker (securityheaders.com, Google CSP Evaluator) to validate your policy. Avoid unsafe-inline in CSP — it negates most of CSP's XSS protection benefit. Refactor inline scripts to external files or use nonces/hashes. For HSTS, start with a short max-age (300 seconds) to test that HTTPS works correctly before increasing to 31536000 and adding includeSubDomains. Only add preload when you are certain all subdomains support HTTPS, as preload is very difficult to reverse.
CORS security requires explicitly allowlisting origins. Never use Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — this combination is explicitly blocked by browsers but servers sometimes return this configuration in error. For APIs that need to accept requests from multiple specific origins, implement origin validation in the server and dynamically echo a validated origin value rather than using wildcard. For APIs called by server-side code only (no browser clients), CORS is irrelevant — the same-origin policy only applies to browser-initiated requests.
Common failure modes
- CSP with unsafe-inline:
script-src 'unsafe-inline'allows all inline scripts, which defeats the primary XSS protection benefit of CSP. - CORS wildcard with credentials: APIs that return
Access-Control-Allow-Origin: *alongside sensitive data (even without explicit credentials) may leak data to cross-origin JavaScript in some configurations. - Missing HSTS on API endpoints: API responses must also send HSTS or the browser may send the initial request over HTTP; HTTPS-only enforcement must apply to all subdomains.
- CSP blocking legitimate third-party scripts: Analytics, A/B testing, and chat widgets require CSP allowlisting; teams add
unsafe-inlineas a quick fix rather than properly allowing specific external sources. - No HSTS max-age rollout plan: Setting
max-age=31536000immediately, before testing all subdomains, can lock out users if a subdomain breaks HTTPS.
Decision checklist
- Are HSTS, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy headers present on all responses?
- Is a Content-Security-Policy configured without
unsafe-inlineorunsafe-eval? - Is CSP violation reporting configured and monitored?
- Is CORS configured with explicit origin allowlisting rather than wildcard?
- Are security headers set at the infrastructure layer (CDN/proxy) to ensure universal coverage?
- Has the application been tested with securityheaders.com or equivalent?
Example use cases
- SPA with CDN: React SPA served from CloudFront; security headers set in CloudFront response headers policy, applying to all cached responses without touching application code; CSP configured to allow scripts from CDN domain only.
- Banking web application: Strict CSP with nonces (per-request from Flask/Django template context); no
unsafe-inline; HSTS preloaded; CSP Report-URI endpoint logs all violations to SIEM for attack detection. - WebAssembly application: COOP + COEP enabled to allow SharedArrayBuffer for WASM threading; Cross-Origin-Resource-Policy: same-origin added to all subresources; tested with browser cross-origin isolation checks.
Related patterns
- Defense in Depth — Security headers are the browser-side defence layer.
- API Security — CORS configuration is part of API security.
- Network Security Architecture — WAF and security headers are complementary controls.