Security Headers
HTTP security headers are one of the simplest and most effective defenses you can add to a web application. They instruct the browser to enable built-in protections against cross-site scripting, clickjacking, MIME sniffing, protocol downgrade attacks, and more. Despite their power, a surprising number of production websites ship without them. This lesson covers every header you should implement, explains what each one prevents, and provides example values you can use today.
Content-Security-Policy (CSP)
The Content-Security-Policy header is the single most powerful security header available. It tells the browser exactly which sources of content are permitted to load on your page. By restricting where scripts, styles, images, fonts, and other resources can originate, CSP dramatically reduces the impact of XSS vulnerabilities — even if an attacker manages to inject malicious HTML.
Example
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'; style-src 'self' 'nonce-xyz789'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'none';
What it prevents
- Cross-site scripting (XSS): Inline scripts and scripts from unauthorized sources are blocked.
- Data injection attacks: Prevents loading of unauthorized resources like tracking pixels or malicious iframes.
- Clickjacking: The
frame-ancestorsdirective prevents your page from being embedded in iframes on other sites.
Strict-Transport-Security (HSTS)
HSTS tells the browser to only access your site over HTTPS, even if the user types http:// or clicks an HTTP link. Once a browser receives this header, it will automatically convert all HTTP requests to HTTPS for the specified duration. This prevents protocol downgrade attacks and cookie hijacking.
Example
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
What it prevents
- Protocol downgrade attacks: An attacker on a shared network cannot force the user's browser to connect over insecure HTTP.
- Cookie hijacking: Session cookies cannot be intercepted over an unencrypted connection.
- SSL stripping: Tools like sslstrip that intercept the initial HTTP request and prevent the HTTPS redirect become ineffective after the first visit.
Key parameters
max-age: Duration in seconds the browser should remember to enforce HTTPS. 63072000 seconds equals two years, the recommended minimum for HSTS preloading.includeSubDomains: Applies the policy to all subdomains as well.preload: Signals to browser vendors that you want to be included in the HSTS preload list, so HTTPS is enforced from the very first visit.
X-Content-Type-Options
This header prevents the browser from trying to MIME-sniff the content type of a response. Without it, a browser might interpret a file differently than the server intended — for example, treating an uploaded text file as HTML and executing scripts within it.
Example
X-Content-Type-Options: nosniff
What it prevents
- MIME confusion attacks: An attacker uploads a file with a
.txtextension but containing HTML and JavaScript. Withoutnosniff, the browser might execute it as HTML. - Drive-by downloads: Prevents the browser from misinterpreting content types in ways that could lead to code execution.
X-Frame-Options
X-Frame-Options controls whether your page can be displayed in a frame, iframe, embed, or object element. This is the primary defense against clickjacking attacks, where an attacker overlays a transparent iframe of your site on top of a malicious page to trick users into clicking hidden buttons.
Example
X-Frame-Options: DENY
What it prevents
- Clickjacking: An attacker cannot embed your login page in a transparent iframe and trick users into entering credentials on what appears to be a different site.
- UI redressing: Prevents visual deception attacks where your legitimate page is overlaid with malicious content.
Options
DENY: The page cannot be displayed in a frame at all. This is the most restrictive and recommended setting.SAMEORIGIN: The page can only be displayed in a frame on the same origin.
frame-ancestors directive is the modern replacement for X-Frame-Options. It is more flexible and supports multiple origins. However, X-Frame-Options should still be set for compatibility with older browsers.
Referrer-Policy
The Referrer-Policy header controls how much referrer information is included with requests. By default, browsers send the full URL of the referring page, which can leak sensitive information like session tokens in URLs, internal paths, or user-specific pages.
Example
Referrer-Policy: strict-origin-when-cross-origin
What it prevents
- Information leakage: Prevents sensitive URL parameters (session IDs, tokens, internal paths) from being sent to third-party sites.
- Privacy violations: Limits what external sites can learn about your users' browsing patterns.
Recommended values
strict-origin-when-cross-origin: Sends origin, path, and query string for same-origin requests; sends only the origin for cross-origin requests when the protocol security level stays the same; sends nothing when navigating to a less secure destination. This is a good default.no-referrer: Never sends the referrer header. Most private, but may break analytics.same-origin: Sends full referrer for same-origin requests, nothing for cross-origin.
Permissions-Policy
Permissions-Policy (formerly Feature-Policy) lets you control which browser features and APIs your page can use. You can disable access to the camera, microphone, geolocation, payment APIs, and many other features that your application does not need. This reduces your attack surface and prevents malicious scripts from accessing sensitive browser APIs.
Example
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()
What it prevents
- Unauthorized feature access: If an XSS vulnerability is exploited, the malicious script cannot access the camera, microphone, or geolocation even if the user has previously granted permission.
- Third-party iframe abuse: Embedded iframes cannot access browser features unless explicitly permitted by the parent page.
Cross-Origin Headers (COOP, COEP, CORP)
The three Cross-Origin headers work together to establish a strong security boundary between your site and external resources. They are particularly important for enabling powerful features like SharedArrayBuffer and high-resolution timers, which browsers have restricted due to Spectre-class vulnerabilities.
Cross-Origin-Opener-Policy (COOP)
Cross-Origin-Opener-Policy: same-origin
Isolates your browsing context so that cross-origin documents cannot interact with your window. This prevents cross-origin attacks that rely on window references (e.g., window.opener).
Cross-Origin-Embedder-Policy (COEP)
Cross-Origin-Embedder-Policy: require-corp
Requires all resources loaded by your page to either be same-origin or explicitly opt in to being loaded cross-origin using CORS or the CORP header. This prevents your page from loading unauthorized cross-origin resources.
Cross-Origin-Resource-Policy (CORP)
Cross-Origin-Resource-Policy: same-origin
Specifies who can load your resources. Setting it to same-origin means only your own pages can load your images, scripts, and other assets. This prevents other sites from hotlinking your resources and mitigates certain data-leaking side-channel attacks.
What they prevent together
- Spectre-class attacks: COOP + COEP together enable cross-origin isolation, which is required for the browser to provide high-resolution timers and
SharedArrayBuffersafely. - Cross-origin data leaks: Prevents your site from being used as a vector for side-channel attacks against other origins.
- Window reference attacks: COOP prevents cross-origin pop-ups from retaining references to your window object.
Putting it all together
Here is a comprehensive set of security headers that provides strong protection for most web applications:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; frame-ancestors 'none';
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
Content-Security-Policy-Report-Only and monitor for violations before enforcing. Test each header individually and verify your application still works correctly.
Resources
- SecurityHeaders.com — Free scanner that grades your security headers from A+ to F
- Mozilla Observatory — Comprehensive security assessment from Mozilla