HTTP security headers are directives that a web server sends to the browser to restrict behaviors that are safe by default but exploitable in an attack. Each header addresses a specific class of attack: protocol downgrade, MIME confusion, clickjacking, information leakage, or cross-origin data access. None of them require changes to application logic. They are configuration choices.
This guide covers each major security header, what it does, the recommended value, what commonly goes wrong, and how to verify the header is actually being served after deployment.
Why Security Headers Matter
Browsers implement a large number of opt-in security restrictions that sites can enable through response headers. These restrictions exist because browsers must maintain backward compatibility with a huge range of existing sites and cannot change default behaviors unilaterally. The security-relevant behaviors are therefore available but not automatic.
Missing security headers are a consistent finding in security audits because the site works fine without them. The absence of HSTS does not break HTTPS. The absence of X-Frame-Options does not break page rendering. Their value is entirely in the protection they provide to users, which is only visible when an attack is attempted.
Strict-Transport-Security (HSTS)
HSTS instructs a browser that has previously visited your site over HTTPS to only access it over HTTPS in the future, for the duration specified in max-age. It prevents protocol-downgrade attacks where an attacker on the network path redirects an HTTP request before the site can redirect it to HTTPS.
Strict-Transport-Security: max-age=31536000; includeSubDomainsThe header is only meaningful when served over HTTPS. A browser that receives HSTS over an HTTP connection must ignore it.
max-age is the number of seconds the browser should remember the HSTS policy. 31536000 (one year) is the commonly recommended value and the minimum required for HSTS preloading.
includeSubDomains extends the policy to all subdomains. This is required for preloading and is generally recommended, but you should verify that all subdomains are actually accessible over HTTPS before enabling it. A subdomain that is HTTP-only will become unreachable to users whose browsers have the HSTS policy cached.
The preload directive signals that the domain should be added to the browser preload list. This is covered in detail in the HSTS Preloading article. Do not add preload until you have verified all subdomains are HTTPS-ready and you understand that removal from the preload list takes months to propagate.
X-Content-Type-Options
MIME-type sniffing is a browser behavior where the browser inspects the content of a response and may treat it as a different content type than the server declared. This creates an attack vector where a server serves a file it believes to be an image, but the browser sniffs it and executes it as JavaScript.
X-Content-Type-Options: nosniffThis header has exactly one valid value: nosniff. When present, the browser will not try to guess the content type and will refuse to load a resource if the declared MIME type does not match the expected type for the context. For example, a script tag that loads a resource with a text/plain content type will be blocked.
This header should be present on all responses, not just HTML documents. It is particularly important for endpoints that serve user-uploaded files.
X-Frame-Options
Clickjacking attacks work by embedding a target page in an invisible frame on an attacker-controlled page, then tricking users into clicking on the target content. The attacker-controlled page receives the click, but it is positioned over the target frame so the user performs an action on the target site without realizing it.
X-Frame-Options: DENY
# or, to allow same-origin framing only:
X-Frame-Options: SAMEORIGINDENY prevents all framing. SAMEORIGIN allows framing only by the same origin. The ALLOW-FROM value is no longer supported in modern browsers and should not be used.
The CSP frame-ancestors directive supersedes X-Frame-Options in browsers that support CSP Level 2. For maximum compatibility, include both. Set frame-ancestors 'none' in your CSP alongside X-Frame-Options: DENY.
Referrer-Policy
By default, browsers include a Referer header in requests that contains the full URL of the page the user navigated from. This can leak sensitive URL parameters, session tokens embedded in URLs, or internal path structures to third-party analytics services, CDNs, and external link destinations.
Referrer-Policy: strict-origin-when-cross-originThis is the current browser default for navigations, but explicitly setting it in a header ensures consistent behavior across all browsers and prevents older browser defaults from leaking full URLs.
Common values and their behavior:
no-referrer: No Referer header is sent. Maximum privacy, but may break some analytics integrations that depend on referrer data.strict-origin-when-cross-origin: Sends the full URL for same-origin requests, origin only for cross-origin requests, nothing if downgrading from HTTPS to HTTP. A reasonable default for most sites.same-origin: Only sends the Referer for same-origin navigations.no-referrer-when-downgrade: The pre-2020 browser default. Sends the full URL for HTTPS-to-HTTPS and HTTP-to-HTTP requests. Leaks full URLs to third-party HTTPS services.unsafe-url: Always sends the full URL. Should not be used.
Permissions-Policy
Permissions-Policy (formerly Feature-Policy) controls which browser features and APIs a page may access. It can restrict the use of camera, microphone, geolocation, payment requests, and other sensitive capabilities, both for the page itself and for any embedded third-party frames.
Permissions-Policy: camera=(), microphone=(), geolocation=()An empty parentheses value () disables the feature for all origins including the page itself. To allow a feature only for the same origin: camera=(self). To allow a specific external origin: camera=(self "https://video.example.com").
The appropriate value depends on your site. A marketing site that never uses camera or microphone should disable them explicitly. An application that uses the camera should restrict it to the necessary origins only. Disabling features your site does not use reduces the attack surface available to compromised third-party scripts.
CORP, COEP, and COOP
Three related headers control cross-origin isolation, which is required to access high-resolution timers and SharedArrayBuffer APIs. They matter primarily for sites that want to isolate themselves from cross-origin reads and from Spectre-style timing attacks.
- Cross-Origin-Resource-Policy (CORP): Controls whether other origins may include this resource. Values are
same-origin,same-site, andcross-origin. Settingsame-originon a resource prevents other origins from loading it, even with a<script>or<img>tag. - Cross-Origin-Embedder-Policy (COEP): Requires that all resources loaded by a page either have a CORP header permitting the load, or have a CORS header. Setting
require-corpenables cross-origin isolation. - Cross-Origin-Opener-Policy (COOP): Controls whether a newly opened window can reference its opener.
same-origin-allow-popupsis the recommended value for sites that need to open payment popups while still achieving isolation.
CORP, COEP, and COOP are not required for most sites and can break third-party integrations if deployed without testing. They are worth evaluating for applications that handle sensitive data and want to isolate themselves from cross-origin reads.
Deployment Checklist
The following headers should be present on HTML document responses for virtually all production sites:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: [your policy]After deploying, verify with curl or a tool like WebDefect that every header is present on actual document responses, including responses served via CDN edge nodes. CDN caching and edge configuration rules can add, strip, or overwrite headers in ways that differ from your origin server configuration.
How WebDefect Evaluates Headers
WebDefect checks for each security header on all HTML document responses discovered during a scan. The check records both the presence and the value of each header. Headers that are present but set to values that provide no protection (such as a CSP with only unsafe-inline) are flagged separately from missing headers, so the finding describes the actual condition rather than just the binary presence or absence.
Severity assignments for header findings:
- Missing HSTS on an HTTPS site: high severity.
- Missing CSP: medium severity (absence of defense-in-depth).
- Effectively permissive CSP (unsafe-inline, wildcards): medium severity.
- Missing X-Content-Type-Options: low severity.
- Missing X-Frame-Options (without CSP frame-ancestors): medium severity.
- Missing Referrer-Policy: low/informational severity.