Join our Newsletter — 33% off our NHI Course
Home› Guides› Security Headers Guide: CSP, HSTS and Cookies
Guide Application Security

Security Headers Guide: CSP, HSTS and Cookies

← All guides
By Lalit Choda, NHI Mgmt Group Updated 17 October 2026 17 min read
On this page

HTTP security headers are the cheapest browser-side controls a web team can deploy, and most sites still ship them half configured: an allowlist CSP that can be bypassed, HSTS without subdomains, session cookies readable by JavaScript. Each header tells the browser to refuse something it would otherwise allow, such as running an injected script, downgrading to HTTP, rendering inside an attacker's frame or sending a cookie cross-site. This guide covers the headers that matter today, the values to start from, the deprecated headers to remove, and how to roll out Content-Security-Policy and HSTS with report-only modes and a rollback path. It is written for application security engineers, web developers and platform teams who own reverse proxies, CDNs and frameworks.

Key takeaways

  • Use a strict CSP, not an allowlist. A nonce or hash policy with 'strict-dynamic', object-src 'none' and base-uri 'none' resists bypasses that host allowlists do not.
  • HSTS preload is a one-way door. Ramp max-age up in stages and confirm every subdomain serves HTTPS before adding preload; removal from browser lists takes months.
  • Session cookies are credentials. Set Secure, HttpOnly, an explicit SameSite value and the __Host- prefix, and send Cache-Control: no-store on authenticated pages.
  • Remove headers that hurt. X-XSS-Protection, Expect-CT, Public-Key-Pins and ALLOW-FROM are deprecated or ignored, and some can create vulnerabilities.
  • Report before you enforce. Content-Security-Policy-Report-Only, COEP report-only and the Reporting-Endpoints header let you see breakage in production before users do.

Security headers change browser behaviour for the response that carries them. Four rules decide whether they work:

  • Headers, not meta tags: frame-ancestors, sandbox and CSP reporting do not work in a <meta> policy, Content-Security-Policy-Report-Only is not supported in <meta>, and X-Frame-Options in a meta tag has no effect. Deliver them as HTTP headers.
  • Every response: attackers frame or sniff whichever URL lacks the header, including error pages, redirects and API responses. In nginx, add_header ... always; is needed for headers to appear on error responses.
  • HTTPS only for some: RFC 6797 section 8.1 says a browser "MUST ignore any present STS header field(s)" received over insecure transport, and Secure cookies cannot be set by http: pages.
  • Defence in depth, not a fix: the OWASP CSP Cheat Sheet treats CSP as a second layer that does not replace output encoding and other XSS prevention. Headers reduce the impact of a bug; they do not remove it.

The values below come from the OWASP HTTP Headers Cheat Sheet, MDN and web.dev. Treat them as a starting point and test each against your application.

HeaderStarting valueWhat it preventsNotes
Content-Security-Policyscript-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none' plus frame-ancestors and form-actionScript injection (XSS), plugin abuse, base tag hijack, clickjackingDeploy as Report-Only first
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preloadSSL stripping, HTTP downgrade, click-through certificate warningsAdd preload only after the ramp-up
X-Content-Type-OptionsnosniffMIME sniffing of uploads and responses into script or HTMLAlso send a correct Content-Type with charset=UTF-8
X-Frame-OptionsDENY (or SAMEORIGIN)Clickjacking in older browsersKeep alongside frame-ancestors
Referrer-Policystrict-origin-when-cross-origin (or no-referrer on sensitive apps)Leaking paths, tokens and IDs in the Referer headerSet explicitly, do not rely on browser defaults
Permissions-Policycamera=(), microphone=(), geolocation=() and other unused featuresAbuse of powerful APIs by injected or embedded codeMDN marks it limited availability
Cross-Origin-Opener-Policysame-originCross-window attacks, XS-Leaks via window.openersame-origin-allow-popups for popup login or payment flows
Cross-Origin-Embedder-Policyrequire-corp (or credentialless)Loading cross-origin resources that did not opt inOnly needed for cross-origin isolation
Cross-Origin-Resource-Policysame-site (or same-origin)Other sites embedding your resources (Spectre-style reads)cross-origin for public CDN assets
Cache-Controlno-store on authenticated and sensitive responsesPersonal data left in shared or browser cachesno-cache still stores, it only revalidates
Set-Cookie (session)__Host-sid=...; Secure; HttpOnly; SameSite=Lax; Path=/Cookie theft by script, network or sibling subdomain; CSRFSameSite=Strict where cross-site entry is not needed

Deprecated and information-leaking headers

HeaderStatusAction
X-XSS-ProtectionDeprecated, non-standard; MDN warns it "can create XSS vulnerabilities in otherwise safe websites"Remove, or send 0 as OWASP advises; rely on CSP
Expect-CTObsoleteRemove (OWASP: do not use)
Public-Key-Pins (HPKP)Removed from major browsersRemove both HPKP headers; use CT monitoring and CAA records
X-Frame-Options ALLOW-FROMObsolete; whole header ignoredUse frame-ancestors
X-Content-Security-Policy, X-WebKit-CSPObsolete prefixed CSP headersRemove; use Content-Security-Policy
Feature-PolicyReplaced by Permissions-PolicyMigrate the syntax
Server, X-Powered-By, X-AspNet-VersionInformation disclosureRemove or set a generic value; for ASP.NET use enableVersionHeader="false"

Content-Security-Policy

CSP Level 3 is still a W3C Working Draft (latest dated 16 September 2026), but its core directives are widely implemented. Google's web.dev guidance states that allowlist policies "can be bypassed in most configurations", because a trusted host such as a CDN or analytics domain often serves JSONP endpoints, old libraries or user content an attacker can call. Strict CSP removes host trust from script loading.

Strict CSP: nonces or hashes

Content-Security-Policy:
  script-src 'nonce-R4nd0mBase64Value' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none'; base-uri 'none';
  frame-ancestors 'none'; form-action 'self';
  report-to csp-endpoint
  • Nonce-based: for server-rendered pages. The nonce must be cryptographically random (web.dev says ideally 128 bits or more), base64 encoded and regenerated for every response. The OWASP CSP Cheat Sheet warns against middleware that adds the nonce to every script tag, which would also bless injected ones.
  • Hash-based: for static or cached pages where a per-response nonce is impractical. List 'sha256-...' values for each inline script; any change, even whitespace, breaks the hash.
  • 'strict-dynamic': lets a trusted script load further scripts it creates, which makes tag managers and widgets workable without listing their hosts.
  • Fallbacks: https: and 'unsafe-inline' are there only for old browsers. Browsers that support 'strict-dynamic' ignore them, and 'unsafe-inline' is ignored when a nonce or hash is present.
  • Refactoring: inline handlers such as onclick and javascript: URLs are blocked. Move them to addEventListener in script files. 'unsafe-hashes' exists for stubborn handlers but weakens the policy.

Directives that matter beyond scripts

DirectiveGroupRecommended use
default-srcFetchFallback for fetch directives not set; 'self' for a non-strict baseline
object-srcFetch'none' to disable plugins
connect-srcFetchRestrict fetch(), XHR and WebSocket destinations to limit exfiltration
base-uriDocument'none' or 'self' to stop injected <base> tags redirecting relative script URLs
sandboxDocumentFor user-generated or untrusted documents; header only
frame-ancestorsNavigation'none', 'self' or named origins; replaces X-Frame-Options in supporting browsers
form-actionNavigation'self' to stop injected forms posting credentials elsewhere
upgrade-insecure-requestsOtherRewrites http: subresources to HTTPS during migrations to remove mixed content
report-to / report-uriReportingreport-uri is deprecated; send both while older browsers remain in use

Report-only and the Reporting API

Declare endpoints with Reporting-Endpoints (MDN lists it as Baseline 2024; it replaces the older Report-To header) and reference them by name. Non-HTTPS endpoints are ignored.

Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports", default="https://example.com/reports"
Content-Security-Policy-Report-Only: script-src 'nonce-...' 'strict-dynamic'; object-src 'none'; base-uri 'none'; report-to csp-endpoint

You can send an enforced policy and a stricter Report-Only policy at the same time, which is how teams tighten an existing CSP without an outage. Add 'report-sample' to get the first 40 characters of blocked inline code, which makes triage much faster. Trusted Types (require-trusted-types-for 'script') complements strict CSP against DOM XSS where browser support allows.

Strict-Transport-Security and preload

HSTS (RFC 6797, November 2012) tells a browser to use HTTPS for a host for max-age seconds and, under section 8.4, to terminate the connection on any certificate error, so users cannot click through a warning. includeSubDomains extends the rule to every subdomain, and max-age=0 removes the policy. The first visit is still exposed (RFC 6797 calls it the bootstrap MITM vulnerability), which is what preload lists solve.

The hstspreload.org submission requirements are a valid certificate, an HTTP to HTTPS redirect on the same host, HTTPS on all subdomains (including www and internal names with DNS records), and a header on the base domain with max-age of at least 31536000, includeSubDomains and preload, including on HTTPS redirects. The list is built into Chrome and reused by other major browsers.

  • Ramp-up: hstspreload.org recommends 300 seconds, then 604800 (one week), then 2592000 (one month), each with includeSubDomains, waiting the full max-age and fixing breakage at each stage before submitting.
  • Rollback: before preload, serve max-age=0 to clear cached policies (only browsers that revisit will see it). After preload, "inclusion in the preload list cannot easily be undone"; removal takes months to reach Chrome users and is not guaranteed elsewhere.
  • Lock-out risk: OWASP warns that a long max-age combined with an expired or revoked certificate can lock users out. Certificate automation is a prerequisite. See the Machine Identity, PKI and Certificate Lifecycle Guide.

Framing, MIME sniffing, referrers and browser features

  • Clickjacking: CSP frame-ancestors is the modern control and supports multiple named origins. Keep X-Frame-Options: DENY or SAMEORIGIN for older clients. ALLOW-FROM is obsolete; MDN notes modern browsers ignore the whole header if they see it.
  • MIME sniffing: X-Content-Type-Options: nosniff stops browsers guessing a type, which matters for user uploads. It only works when every response carries the correct Content-Type.
  • Referrer leakage: URLs carry reset tokens, document IDs and search terms. strict-origin-when-cross-origin sends only the origin cross-site; no-referrer suits admin consoles and health or finance portals.
  • Permissions-Policy: allowlists are *, () (disabled everywhere), self or quoted origins, for example geolocation=(self "https://maps.example.com"). An iframe allow attribute can only narrow what the parent header permits. MDN lists 51 directives, including payment, usb, display-capture and publickey-credentials-get.
  • Permissions policy violations: add ;report-to=endpoint to a directive, or define a default endpoint, to receive permissions-policy-violation reports. A Permissions-Policy-Report-Only variant also exists for testing. Violations usually mean an embedded third party is calling an API you disabled, so review them before loosening the policy.

Cross-origin isolation and CORS

COOP, COEP and CORP limit Spectre-class side channels that let other origins read your data. Together, COOP same-origin and COEP require-corp make a page cross-origin isolated (self.crossOriginIsolated returns true), which is required for SharedArrayBuffer and high-resolution timers. Most sites need COOP and CORP, not COEP.

  • COOP: same-origin puts the page in its own browsing context group, cutting window.opener links to cross-origin windows. Popup-based SSO, OAuth and payment flows that rely on window.opener can break; same-origin-allow-popups keeps popups the page opens.
  • COEP: require-corp blocks cross-origin resources that do not send CORP or CORS headers. Test with Cross-Origin-Embedder-Policy-Report-Only first. credentialless loads them without cookies; web.dev listed it as Chromium-only when written, so check current support.
  • CORP: set same-origin or same-site on authenticated API responses and private assets, and cross-origin only on assets meant for embedding.

CORS is different: it relaxes the same-origin policy rather than adding protection. The common misconfigurations map to CWE-942 (Permissive Cross-domain Security Policy with Untrusted Domains):

  • Reflecting the Origin header: echoing any Origin back in Access-Control-Allow-Origin with Access-Control-Allow-Credentials: true lets any site read authenticated responses. Match against an exact allowlist.
  • Wildcards with credentials: MDN states browsers block * for credentialed requests, which pushes developers into reflection. Use * only for public, unauthenticated APIs.
  • Loose matching: suffix or regex checks that accept evil-example.com or null. Compare full origins.
  • Missing Vary: Origin: when the allowed origin is chosen dynamically, caches can serve one origin's CORS response to another.

Cookies, caching and logout

Session cookies, refresh tokens held in cookies and CSRF tokens are bearer credentials: whoever presents them is the user. MITRE ATT&CK T1539 Steal Web Session Cookie covers their theft, and header settings decide how easily it happens. See the Token and Session Security Guide.

Attribute or prefixEffectWeakness if missing
SecureSent only over HTTPSCWE-614: cookie exposed on any HTTP request
HttpOnlyNot readable by document.cookieCWE-1004: XSS can read and exfiltrate the session
SameSite=Strict / LaxWithheld on cross-site requests (Lax allows top-level GET navigations)CWE-1275: CSRF and cross-site leaks
SameSite=NoneSent cross-site; requires SecureOnly for genuine embedded or federated use
No DomainHost-only cookie, not sent to subdomainsA compromised or taken-over subdomain receives the session
__Host- prefixBrowser rejects it unless Secure, no Domain and Path=/Sibling subdomains can overwrite or fixate the cookie
__Secure- prefixMust be set with Secure from HTTPSWeaker than __Host-; use where a Domain is needed
__Http- / __Host-Http-Also require HttpOnly, so script cannot set themNewer prefixes; check browser support before relying on them
  • Do not rely on Lax-by-default: MDN says only "some browsers" default to Lax, with a two-minute POST exception. Set SameSite explicitly.
  • Path is not a boundary: MDN states Path "is not intended as a security measure". Separate sensitive apps by host, not path.
  • Cache-Control: send Cache-Control: no-store on authenticated pages and API responses with personal data, and private where per-user caching is acceptable. OWASP notes no-cache does not prevent storage.
  • Clear-Site-Data on logout: Clear-Site-Data: "cache", "cookies", "storage" on the logout response removes local copies. Invalidate the session server-side as well; clearing the browser does not revoke a stolen cookie.

Where to set headers and which options to choose

LayerGood forTrade-off
Application or framework (Helmet, Spring Security, Django middleware)CSP nonces, per-route Cache-Control, cookie attributesEach app team must configure it; drift between services
Reverse proxy or ingress (nginx, Envoy, Kubernetes ingress)HSTS, nosniff, Referrer-Policy, COOP, CORP across many appsCannot generate nonces; risk of duplicate headers with the app
CDN or edge rules / workersOrganisation-wide baseline, fast rollback, legacy appsEdge must not cache responses with per-user nonces or cookies
  • Nonce or hash: nonce for dynamic server-rendered apps; hash for static sites and single-page apps served from a CDN.
  • Strict CSP or allowlist: choose strict wherever you control the templates. An allowlist with default-src 'self' and no 'unsafe-inline' is an acceptable interim for legacy apps, if no allowed host serves user content or JSONP.
  • Preload or not: preload when you own the whole domain tree and certificate renewal is automated. Large organisations with forgotten HTTP-only subdomains should fix those first or use HSTS without includeSubDomains on the apex until they do.
  • Small vs large: small teams can set the baseline at the CDN or host and check it with Mozilla Observatory; large organisations should publish a header standard and enforce it in ingress templates and CI.

Rolling out without breaking production

  1. Inventory: scan every hostname, including APIs, login, error pages and redirects, and record current headers. See the Attack Surface Management Guide.
  2. Quick wins: add nosniff, Referrer-Policy, X-Frame-Options or frame-ancestors, CORP and cookie attributes, and remove deprecated and banner headers. These rarely break sites.
  3. CSP in report-only: deploy a strict policy as Content-Security-Policy-Report-Only with a reporting endpoint, refactor inline handlers, and watch reports for at least one full release cycle.
  4. Enforce CSP in rings: enforce on low-risk routes or a percentage of traffic first, keeping the report-only header for the next tightening step.
  5. HSTS ramp: 300 seconds, one week, one month, then one or two years, with includeSubDomains once all subdomains are confirmed, and preload last.
  6. Cross-origin isolation: only if the app needs it; COEP report-only first, then enforce.
  7. Rollback: keep header sets in version-controlled configuration with a feature flag per header. CSP and Permissions-Policy roll back instantly; HSTS rolls back only by serving max-age=0 and waiting, and preload barely at all.
  8. Regression tests: assert headers in CI and in synthetic monitoring so a framework upgrade or new proxy cannot silently drop them.

Detection and measurement

CSP and Permissions-Policy reports are a detection source, not just a debugging aid. A sudden burst of script-src violations from one page can be an injection attempt or a compromised third-party script; route reports to the SIEM with page, blocked URI and sample. See the SIEM Guide. Rate-limit and validate the endpoint, because reports are unauthenticated input.

  • Coverage: percentage of internet-facing hostnames returning the baseline header set, measured externally.
  • Strict CSP enforcement: percentage of applications enforcing a nonce or hash policy without 'unsafe-inline' or 'unsafe-eval'.
  • HSTS: percentage of registrable domains with max-age of at least one year and includeSubDomains, and how many are preloaded.
  • Cookie hygiene: percentage of session cookies with Secure, HttpOnly, explicit SameSite and a __Host- prefix.
  • Violation trend: CSP violations per 10,000 page views by directive, which should fall during refactoring and stay flat after enforcement.
  • Regression rate: releases that removed or weakened a header, caught in CI before production.

Common failure patterns

  • Trusting a CDN host in script-src: an allowlisted public CDN serves every package on it. The Lottie Player npm compromise pushed a wallet drainer to websites through a legitimate library, which a host allowlist would not stop. Pin versions, self-host or use Subresource Integrity.
  • Report-only forever: a Report-Only policy nobody reads is counted as "having CSP".
  • Headers on the homepage only: one-URL scans miss login, API and error responses without frame-ancestors or no-store.
  • Preloading too early: an internal subdomain on HTTP or with a private CA becomes unreachable for every browser, and the fix takes months.
  • Origin reflection in CORS: a helper that copies the request Origin into the response with credentials enabled exposes every authenticated API to any website.
  • Cookies scoped to the parent domain: Domain=example.com sends the session to every subdomain, including marketing sites and abandoned hosts open to takeover.

Framework and weakness mapping

FrameworkReferenceHow headers apply
NIST CSF 2.0PR.DS-02, PR.PS-01, PR.PS-06Data in transit (HSTS), configuration management (header baseline), secure development (CSP refactoring)
NIST SP 800-53 Rev. 5SC-8, SC-23, CM-6Transmission confidentiality and integrity, session authenticity, configuration settings
ISO/IEC 27001:2022 Annex AA.8.9, A.8.26, A.8.28Configuration management, application security requirements, secure coding
CIS Controls v8.1Control 3 (Safeguard 3.10), Control 16Encrypt sensitive data in transit; application software security
MITRE ATT&CKT1539 Steal Web Session CookieHttpOnly, Secure, __Host- and short sessions raise the cost of cookie theft
MITRE CWECWE-614, CWE-1004, CWE-1275, CWE-942, CWE-1021Cookie flags, SameSite, permissive CORS or CSP, clickjacking (rendered UI layers)

For how these frameworks relate to each other, see the Control Mapping Guide.

Practitioner checklist

  • Inventory headers on every internet-facing hostname, including login, API, redirect and error responses.
  • Deploy a nonce or hash CSP with 'strict-dynamic', object-src 'none', base-uri 'none', frame-ancestors and form-action, in report-only first.
  • Stand up a Reporting-Endpoints collector, keep report-uri for older browsers, and send reports to the SIEM.
  • Ramp HSTS to at least one year with includeSubDomains; preload only when every subdomain is HTTPS and certificates renew automatically.
  • Set session cookies as __Host-, Secure, HttpOnly and explicit SameSite, with no Domain attribute.
  • Send Cache-Control: no-store on authenticated responses and Clear-Site-Data plus server-side session revocation on logout.
  • Add nosniff, Referrer-Policy, Permissions-Policy for unused features, COOP and CORP.
  • Replace CORS origin reflection with an exact allowlist, add Vary: Origin, and never combine * with credentials.
  • Remove X-XSS-Protection, Expect-CT, HPKP, Server and X-Powered-By values that disclose versions.
  • Assert the header baseline in CI and synthetic monitoring so regressions fail the build.

Standards and references

Related NHI Mgmt Group resources: Token and Session Security Guide · Enterprise Browser and Browser Security Guide · Open Source Security Guide · TLS Inspection Guide · Attack Surface Management Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 17 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org