Browser-enforced security headers remain one of the highest-leverage, lowest-cost defenses a website can deploy. Configured correctly, Content Security Policy, CORS, and a handful of supporting headers close off entire classes of attack without touching application code.
A strict Content Security Policy with per-request nonces, rolled out in report-only mode first, is the single most effective header-based defense against cross-site scripting.
Key Takeaways
- CSP with nonces is the strongest defense against cross-site scripting, but should always roll out in report-only mode first.
- CORS should allow explicit trusted origins only, never a wildcard combined with credentials.
- Complementary headers like HSTS, X-Content-Type-Options, and Referrer-Policy close gaps CSP alone does not cover.
- Headers should live in version control and be monitored with a reporting endpoint, not set once and forgotten.
How We Chose These
These eight practices were selected for their direct impact on the most common web attack vectors — cross-site scripting, cross-origin data leakage, and protocol downgrade attacks — and for having mature, well-documented implementation paths supported by browser vendors and standards bodies.
1. Roll Out CSP in Report-Only Mode First
Content Security Policy is the single most effective browser-side control for limiting the blast radius of injected content, but an aggressive policy shipped straight to enforcement will break legitimate functionality on any non-trivial site. The correct rollout path is Content-Security-Policy-Report-Only with a reporting endpoint configured, monitored for a period while violations are triaged and legitimate sources added, before switching to enforcing mode. Guidance from the MDN Web Docs CSP reference covers the full directive syntax and reporting setup.
2. Use Strict-Dynamic With Per-Request Nonces
Rather than maintaining a fragile allowlist of trusted script domains, modern CSP practice issues a random nonce with every server response and only executes inline or external scripts carrying a matching nonce attribute. Paired with strict-dynamic, this lets trusted first-party scripts load additional scripts dynamically without reopening the policy to arbitrary third-party injection. The tradeoff is implementation complexity: every template that emits a script tag needs to thread the nonce through consistently, which is easier in frameworks with centralized rendering than in legacy multi-template stacks.
3. Lock CORS Down to Explicit Trusted Origins
Cross-Origin Resource Sharing should specify exact trusted origins in Access-Control-Allow-Origin rather than a wildcard, and a wildcard must never be combined with Access-Control-Allow-Credentials: true, since browsers block that combination for good reason. Only the HTTP methods and headers actually needed should be permitted, and preflight OPTIONS responses should be tested explicitly, not assumed to work because the main request does. The OWASP CORS guidance is a useful reference for common misconfiguration patterns.
4. Enforce HTTPS With HSTS and Preload
Strict-Transport-Security tells browsers to never attempt an insecure connection to a domain again, closing the window for protocol-downgrade and cookie-hijacking attacks. Best practice in 2026 is a max-age of at least six months, the includeSubDomains directive, and submission to the browser-maintained HSTS preload list once the policy is stable, so even a user's very first visit is protected. The tradeoff is that preload is effectively permanent and hard to reverse quickly, so it should only be added once HTTPS coverage across all subdomains is confirmed.
5. Set X-Content-Type-Options: nosniff
This single header, applied globally, stops browsers from trying to guess a file's content type based on its contents rather than its declared MIME type — a behavior attackers have historically exploited to get a browser to execute a file as script when the server intended it as a harmless upload. It has effectively no downside and no compatibility cost, which is why it belongs on the list of headers every site should ship regardless of how far along the rest of its security hardening is.
6. Set a Conservative Referrer-Policy
The default referrer behavior in many browsers leaks the full originating URL, including query parameters, to every link a user clicks and every third-party resource a page loads. Setting Referrer-Policy: strict-origin-when-cross-origin keeps full referrer detail for same-origin navigation while trimming it to just the origin for cross-origin requests, which limits accidental leakage of session tokens or internal paths embedded in URLs without breaking analytics that rely on referrer data.
7. Add Cross-Origin Isolation Headers
Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy isolate a page's browsing context from other origins at the process level, closing side-channel attack classes like Spectre-style timing attacks and preventing malicious pop-ups from reaching back into the opening window. These headers are more involved to deploy than the others on this list, since they can break embeds and third-party widgets that were not built with isolation in mind, so they are usually rolled out after the simpler headers are already stable.
8. Treat Headers as Code and Monitor Continuously
Security headers configured through a dashboard and never revisited drift out of date as the application changes. The more durable approach is keeping header configuration in version control alongside the application, reviewing changes through the same pull request process as any other code, and keeping the CSP reporting endpoint active permanently, not just during initial rollout, so new violations surface automatically as the site evolves.
Comparison Table
| Header / Policy | Primary Threat Mitigated | Implementation Effort |
|---|---|---|
| CSP (report-only rollout) | Cross-site scripting (XSS) | High |
| CSP with nonces + strict-dynamic | Script injection | High |
| CORS origin allowlisting | Cross-origin data leakage | Medium |
| HSTS with preload | Protocol downgrade, cookie theft | Low-medium |
| X-Content-Type-Options | MIME sniffing attacks | Low |
| Referrer-Policy | URL and session data leakage | Low |
| COOP / COEP isolation | Side-channel attacks | High |
| Headers-as-code + monitoring | Configuration drift | Ongoing |
How to Choose
Teams with limited security engineering time should start with the low-effort, no-downside headers: X-Content-Type-Options, Referrer-Policy, and HSTS. Teams with a modern, centralized rendering stack should move to CSP with nonces next, since the payoff against XSS is the largest on this list. Organizations with mature CI/CD and dedicated security review should add cross-origin isolation headers and formal headers-as-code monitoring, since both require more coordination to deploy without breaking existing functionality.
FAQ
Should CSP be enforced immediately or rolled out gradually?
Gradually. Deploy in report-only mode first with a reporting endpoint configured, fix violations as they surface, and only switch to enforcing mode once the violation rate has dropped to near zero.
Is a wildcard CORS policy ever safe to use?
Only for fully public, unauthenticated resources with no credentials involved. A wildcard combined with cookies or authentication headers is unsafe and is blocked by modern browsers for exactly that reason.
Do these headers replace the need for other security practices?
No. Headers reduce the impact of specific browser-side attack classes, but they complement, rather than replace, secure coding practices, dependency management, and server-side input validation.
