HTTP Security Headers: What to Set on Every Website, and Why

13 min read
HTTP security headers

Security headers are small HTTP response directives that tell browsers how to handle your website: which scripts to trust, whether to allow framing, when to force HTTPS, and which browser features to disable. They prevent many common attacks before they ever reach your application.

Yet adoption remains uneven. The 2024 Web Almanac found that even the most widely adopted security header, X-Content-Type-Options, appears on only about half of websites. HSTS, X-Frame-Options, and Permissions-Policy are used even less often. Closing that gap is one of the simplest ways to strengthen your site’s security posture.

The blog covers the HTTP security headers every production website should set and the safest order to deploy them. 

When this guide applies

Security headers are designed to protect against browser-based attacks. They help reduce the risk of:

  1. Cross-site scripting (XSS)
  2. Clickjacking
  3. MIME-type confusion
  4. Protocol downgrade attacks
  5. Third-party data leakage
  6. Spectre-class side-channel attacks

They do not protect against:

  1. SQL injection
  2. Broken authentication
  3. TLS vulnerabilities
  4. Attacks that never pass through a browser, such as APIs or scrapers

Think of them as an additional layer of protection rather than a replacement for secure application code. 

Every production website should deploy a baseline set of security headers, whether it’s a marketing site, ecommerce store, or SaaS application. Additional tuning typically happens at the Content Security Policy (CSP) level. For payment pages, PCI DSS 4.0 Requirement 6.4.3 also requires script inventory and integrity controls, with CSP and Subresource Integrity serving as the primary technical controls. 

Prerequisites

Before adding any security header, verify these foundations:

  1. HTTPS everywhere: HTTP-only origins cannot deliver HSTS meaningfully, and mixed-content warnings on HTTPS pages undermine the entire model. Confirm HTTPS coverage across all subdomains before setting includeSubDomains.
  2. A header-injection layer you control: Reverse proxy (nginx, Apache), CDN edge (Cloudflare, Fastly), or framework middleware (Next.js, Express, Django) where headers can be added globally. Identify whether your layer is at origin, CDN edge, or both, since CDN-injected headers can silently conflict with origin headers.
  3. A third-party script inventory: Before writing a CSP, list every external script, stylesheet, font, and API endpoint your pages load: analytics, chat, ads, fonts, payments, support. CSP without this audit will break pages on first enforcement.
  4. A CSP reporting infrastructure: Stand up a report-to endpoint or use a third-party CSP report aggregator before enforcement. Deploy as Content-Security-Policy-Report-Only first, collect for one to two weeks, then switch on.
  5. A staging environment that mirrors production third-party integrations: CSP and HSTS misconfigurations are visible only when real traffic hits real widgets.

The headers every production site should set

The eight headers below are the baseline. Adoption percentages reference the 2024 Web Almanac.

Content-Security-Policy

CSP is the central XSS defense and the hardest header to deploy correctly. Adoption was 19% of all hosts in 2024, up from 15% in 2022. The MDN CSP guide and OWASP Cheat Sheet both recommend a nonce-based, strict-dynamic pattern for dynamic applications:

Content-Security-Policy:

script-src ‘nonce-{RANDOM}’ ‘strict-dynamic’;

object-src ‘none’;

base-uri ‘none’;

  • The nonce is a cryptographically random, base64-encoded value generated fresh per HTTP response. Include it in the script-src directive and in the nonce=”…” attribute of every legitimate <script> tag. strict-dynamic propagates trust from the nonce-bearing root script to any scripts it dynamically loads, which is useful for SPAs that load lazy chunks.
  • For static pages where nonces are impractical, use SHA-256/384/512 hashes of inline script bodies. Never combine ‘unsafe-inline’ with nonces (older browsers ignore the nonce and revert to unsafe behavior). Avoid ‘unsafe-eval’; if a legacy bundler requires it, replace the bundler.
  • Always deploy Content-Security-Policy-Report-Only first, monitor for one to two weeks across real traffic, then promote to enforcement. The non-negotiable rollout pattern in this guide.

Strict-Transport-Security (HSTS)

HSTS stops SSL-stripping MITM attacks by telling the browser to refuse any HTTP connection to the domain for max-age seconds. Once received over HTTPS, the browser internally redirects all subsequent HTTP requests to HTTPS before they leave the device. Spec: RFC 6797. Adoption: 30% of mobile responses in 2024.

Recommended production value:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Warning: HSTS preload is effectively irreversible

Submitting to the hstspreload.org browser-baked list locks every preloaded browser into HTTPS-only access for your domain. Removal takes months to propagate. Any subdomain that cannot serve HTTPS becomes unreachable. Test includeSubDomains thoroughly in staging, ramp max-age from 300 seconds to 31,536,000 over weeks, and verify every DNS entry before submitting to the preload list.

X-Frame-Options and CSP frame-ancestors

X-Frame-Options stops clickjacking by controlling whether your page can be embedded in an iframe. The directive set is DENY, SAMEORIGIN, or ALLOW-FROM uri (the last is deprecated and ignored by all modern browsers).

CSP’s frame-ancestors directive (Level 2+) supersedes X-Frame-Options. It supports multiple sources, which X-Frame-Options cannot. When both are present, modern browsers ignore X-Frame-Options and enforce frame-ancestors. Recommended production posture: set both, for defense in depth and legacy coverage.

X-Frame-Options: DENY

Content-Security-Policy: frame-ancestors ‘none’

For sites that allow specific partner embeds:

X-Frame-Options: SAMEORIGIN

Content-Security-Policy: frame-ancestors ‘self’ https://trusted-partner.com

X-Content-Type-Options

The easiest header in the guide. One value (nosniff), no directives, zero misconfiguration risk:

X-Content-Type-Options: nosniff

It prevents browsers from second-guessing the declared Content-Type and executing an uploaded image file as JavaScript. Set on all responses, not just HTML. Adoption leads all security headers at ~50%.

Referrer-Policy

Without an explicit Referrer-Policy, browsers send the full URL (including path and query string) in the Referer header on cross-origin requests, which can leak session tokens, user IDs, or internal paths to third-party servers. The OWASP-recommended value:

Referrer-Policy: strict-origin-when-cross-origin

Modern browsers default to this when no header is set, but the explicit declaration guarantees consistent behavior across browser versions.

Permissions-Policy

Permissions-Policy (formerly Feature-Policy) controls which powerful browser APIs (geolocation, camera, microphone, USB, payment, full-screen) the page and its iframes may invoke. It is open by default: absent the header, all features are accessible.

Recommended baseline (disable everything you do not use):

Permissions-Policy: geolocation=(), camera=(), microphone=(),

  interest-cohort=(), browsing-topics=(), payment=(),

  usb=(), display-capture=()

Adoption is under 10%, partly because Firefox has limited support for many directives. Set the header regardless; it works in Chromium browsers, and parity improves over time.

Cache-Control on sensitive responses

For authenticated routes, banking, healthcare, admin dashboards, invoices, anything user-specific:

Cache-Control: no-store, private

no-store means no cache may store any copy of the response. private restricts caching to the end user’s browser only. Materially different from no-cache, which permits storage and only requires revalidation before serving. no-cache is wrong for genuinely sensitive data.

Clear-Site-Data on logout

On logout endpoints, set:

Clear-Site-Data: “cache”, “cookies”, “storage”

This purges browser-stored cookies, localStorage, sessionStorage, IndexedDB, service workers, and cache for the origin. Only works over HTTPS.

A note on what to remove: 

  • X-XSS-Protection is deprecated. The XSS Auditor was removed from Chrome in version 78; Firefox never implemented it; all major browsers now ignore the header. 
  • Remove it from server configuration. In some edge cases it created vulnerabilities on older browsers. Replace with a strict CSP.

The deployment sequence (in order)

Some steps are not safely reversible. Deploy in this order:

  1. Foundation headers (zero risk): Deploy immediately: X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or SAMEORIGIN), Referrer-Policy: strict-origin-when-cross-origin. No application-specific configuration; no breaking risk.
  1. HTTPS verification: Before deploying HSTS, verify every page on every subdomain is HTTPS-clean. Audit all DNS entries.
  1. HSTS sentinel: Deploy Strict-Transport-Security: max-age=300 (five minutes). Watch for breakage for several days. Ramp to max-age=31536000; includeSubDomains only after the sentinel period is clean.
  1. HSTS preload (optional, last): Add preload only after exhaustive subdomain HTTPS verification and explicit intent to submit at hstspreload.org. Removal is slow.
  1. CSP in Report-Only: Conduct the third-party script inventory. Deploy Content-Security-Policy-Report-Only with the candidate policy. Monitor for one to two weeks. Tune until violations are zero or all accounted for. Then promote to enforcement.
  1. Permissions-Policy: Disable features your application does not use. Test embedded iframes (maps, videos, chat) thoroughly.
  1. Cross-origin isolation (only if you need it): COOP, COEP, and CORP are the cluster needed for SharedArrayBuffer and high-resolution timers. Most sites do not need them. If you do, set COOP: same-origin-allow-popups first (preserves OAuth popups), then layer COEP: credentialless (lighter coordination cost than require-corp).
  1. Cache-Control + Clear-Site-Data: Set no-store, private on all authenticated routes. Set Clear-Site-Data on logout endpoints.

Validation: what a clean posture looks like

Five tools to know:

  1. SecurityHeaders.com is the fastest free check; A+ to F grades; surfaces missing/present headers. Does not crawl authenticated pages and does not evaluate policy quality beyond presence.
  2. Mozilla Observatory adds TLS and cookie evaluation alongside headers; numerical score.
  3. CSP Evaluator (Google) evaluates CSP policy quality specifically. The tool that breaks “present-but-protects-nothing” deployments.
  4. OWASP ZAP provides full active and passive scanning, authenticated, and scriptable. Catches what passive header checkers miss.
  5. Lighthouse / Chrome DevTools are CI-integrated; basic header checks plus runtime CSP violation visibility.

A clean A+ posture: strong CSP with nonce or hash and no unsafe-*; X-Frame-Options DENY/SAMEORIGIN or frame-ancestors limited; X-Content-Type-Options nosniff; Referrer-Policy strict-origin-when-cross-origin; Permissions-Policy with unused features blocked; HSTS at least six months with includeSubDomains.

What tools miss: policy quality (an allowlist CSP with unsafe-inline passes as “present”), authenticated-page headers, application-level nonce-generation errors, and CDN-origin header conflicts. Audit beyond the score.

Platform patterns

Snippets for the most common production stacks.

nginx:

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 “geolocation=(), camera=(), microphone=()” always;

add_header Cross-Origin-Opener-Policy “same-origin-allow-popups” always;

The always parameter ensures headers ship on error responses (4xx, 5xx) too.

Apache (.htaccess or VirtualHost):

Header always set Strict-Transport-Security “max-age=63072000; includeSubDomains; preload”

Header always set X-Content-Type-Options “nosniff”

Header always set X-Frame-Options “DENY”

Header always set Referrer-Policy “strict-origin-when-cross-origin”

Header always set Permissions-Policy “geolocation=(), camera=(), microphone=()”

Requires mod_headers enabled.

Cloudflare: Use Transform Rules → Response Header Transform → Set static (never “Add”, which duplicates headers when the origin also sends them). HSTS has a dedicated toggle in SSL/TLS → Edge Certificates; enable it there to keep edge and origin in agreement.

Vercel (Next.js): Use next.config.js headers(). Next.js does not set security headers by default; CSP, X-Frame-Options, and HSTS must be explicitly configured.

Netlify: Use the _headers file at site root or netlify.toml.

Node.js (Express): helmet middleware sets secure defaults for most headers. Override CSP with an application-specific policy; helmet’s default CSP is intentionally conservative.

The mistakes that turn good headers into broken sites

These are the failure modes that show up most often in real deployments. Each correctly diagnosed prevents a postmortem.

  1. unsafe-inline or unsafe-eval retained “temporarily”: Both directives get added to unblock a broken build and then left in production indefinitely, eliminating CSP’s XSS protection entirely. The correct fix is to move inline scripts to external files or adopt nonces, never to leave the directives in place.
  2. HSTS preload submitted before subdomain HTTPS coverage is complete: Any subdomain that cannot serve HTTPS becomes completely inaccessible to users with preloaded browsers. Audit every DNS entry before submission.
  3. Duplicate CSP headers from origin and CDN both injecting: Browsers enforce the intersection of all policies (most restrictive wins), which usually means the page breaks in subtle ways. Use CDN “Set” operations, not “Add.”
  4. Permissions-Policy bypassed at the iframe: A document-level Permissions-Policy does not stop an embedded iframe from using a feature if the iframe’s own allow attribute explicitly grants it. Audit iframe attributes alongside the document policy.
  5. Ignoring CSP reports after rollout: The violation reporting endpoint captures active exploitation attempts, not just misconfigurations. Operators who stop monitoring after the initial tuning period miss real attack signals.
  6. Confusing no-cache with no-store for sensitive data: no-cache permits storage and only requires revalidation before serving. no-store is the directive that prevents storage entirely. Use no-store for genuinely sensitive responses.
  7. Deploying security headers only at the CDN edge: Headers set only at the edge may not be present on responses served directly from origin to internal tools, monitoring, or when the CDN is bypassed. Set at origin; let the CDN forward or override consistently.
  8. Keeping X-XSS-Protection: 1; mode=block in configuration: Still appears in many starter templates including some dated Vercel and framework defaults. The header is deprecated and does nothing in modern browsers. Remove it.

Why this matters beyond security

Security headers do more than reduce security risk. HTTPS has been a Google ranking signal since 2014, and HSTS helps ensure browsers consistently connect to the HTTPS version of your site, reducing the chance of protocol downgrades and duplicate HTTP/HTTPS access patterns.

For AI search and automated crawlers, a secure technical posture also contributes to trust. While there is no published evidence that security headers are direct ranking factors for AI engines, HTTPS and well-configured security headers reinforce the signals of a well-maintained website.

There is also a user experience benefit. Browser security warnings and mixed-content errors reduce trust and can hurt conversions. HSTS prevents protocol downgrades, while CSP’s upgrade-insecure-requests directive upgrades eligible resource requests to HTTPS automatically.

Finally, security headers support compliance. PCI DSS 4.0 Requirement 6.4.3 requires script inventory and integrity controls on payment pages, with CSP and Subresource Integrity forming key technical controls. The OWASP HTTP Headers Cheat Sheet and OWASP ASVS V14.4 remain the baseline references used by many security teams and auditors.

A secure website is easier for browsers, search engines, and AI platforms to trust. If you’re reviewing your site’s overall technical readiness, ReSO can help identify the changes that have the greatest impact on discoverability and AI visibility. Book a call to know more.

FAQs

What do I do if my pages break after enforcing CSP?

Pages usually break because CSP blocks inline scripts without nonces or hashes, or third-party scripts missing from the allowlist. Check the violation message in DevTools → Console, then either add the domain to script-src or move inline JavaScript to an external file with a nonce. For sites with many widgets, use strict-dynamic with nonces so trusted scripts can load additional resources.

How do I recover from an HSTS lockout?

Fix the underlying TLS certificate first. For preloaded domains, there is no client-side workaround. For non-preloaded domains, users can clear the HSTS entry in Chrome (chrome://net-internals/#hsts) or Firefox by editing SiteSecurityServiceState.txt in the profile folder.

Do security headers affect SEO directly?

Indirectly, HTTPS has been a confirmed Google ranking signal since 2014, and HSTS helps prevent HTTP/HTTPS fragmentation. There is no published evidence that other security headers directly influence rankings, but they improve user trust and reinforce a well-maintained technical posture.

Should I use Permissions-Policy if Firefox does not support most directives?

Yes, Permissions-Policy works across Chromium browsers, which account for most web traffic, and Firefox support continues to improve. It costs nothing to deploy, so there is little reason to wait for complete browser parity.

Can a CDN inject security headers without the origin doing anything?

Yes, but it can create inconsistencies. Headers applied only at the CDN may be missing when traffic bypasses the edge, while duplicate origin and CDN headers can conflict. The safest approach is to configure headers at the origin and let the CDN override them using Set, not Add.

Mohit Gupta

Mohit’s career spans a diverse range of online and offline businesses, where he has consistently taken ideas from zero to scale with a blend of strategic clarity and disciplined execution. His experience ranges from running profitable startup operations to leading growth, operations, and market expansion initiatives across multiple business models. Today, as Co-Founder at ReSO, Mohit brings strong operational leadership together with an AI-driven go-to-market approach to help businesses increase their search visibility. Known for his calm head, structured thinking, and problem-solving instinct, he brings order to complexity and momentum to every initiative.

9 min read

The myth: Google’s March 2024 core update, the integration of the Helpful Content System into Google’s core ranking systems, and

9 min read

Across 212 AI visibility audits, ReSO found that AI engines rarely agree on which brands to recommend. Only 5.4% of

11 min read

Across 212 AI visibility audits and roughly 508,000 citations from ChatGPT, Perplexity, and Google AI Overviews, one pattern appeared consistently: