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-ancestors directive prevents your page from being embedded in iframes on other sites.
Tip: CSP is so important that it has its own dedicated deep-dive lesson in this course. See CSP: Content Security Policy Deep Dive for a step-by-step implementation guide.

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 .txt extension but containing HTML and JavaScript. Without nosniff, 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.
Note: The CSP 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 SharedArrayBuffer safely.
  • 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
Warning: Do not blindly copy security headers into production without testing. A restrictive CSP can break legitimate functionality. Start with Content-Security-Policy-Report-Only and monitor for violations before enforcing. Test each header individually and verify your application still works correctly.

Resources