HTTP Security Headers Reference

Every security header explained with recommended values, server configuration examples, and common misconfigurations to avoid.

Why Security Headers Matter

HTTP security headers are response headers that instruct the browser to enable or enforce security mechanisms. They are a zero-cost defense layer — adding a few lines to your server configuration can prevent entire classes of attacks including XSS, clickjacking, MIME confusion, protocol downgrade, and information leakage. Despite being easy to implement, the majority of websites still fail to set critical security headers.

Strict-Transport-Security (HSTS)

HSTS forces the browser to use HTTPS for all future requests to your domain, preventing protocol downgrade attacks and cookie hijacking over HTTP.

Recommended value:

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

max-age=31536000 tells the browser to remember the HTTPS-only policy for one year. includeSubDomains applies the policy to all subdomains. preload signals eligibility for browser HSTS preload lists. Start with a short max-age (e.g., 300) during testing, then increase to one year after confirming everything works.

Nginx: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Apache: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

X-Content-Type-Options

Prevents MIME-type sniffing, which can lead to XSS when browsers interpret non-script files as executable JavaScript.

Recommended value:

X-Content-Type-Options: nosniff

There is only one valid value. This header is simple and has no compatibility concerns. Set it on every response.

Nginx: add_header X-Content-Type-Options "nosniff" always;

Apache: Header always set X-Content-Type-Options "nosniff"

X-Frame-Options

Controls whether your page can be embedded in an iframe, preventing clickjacking attacks where an attacker overlays your page with a transparent frame to steal clicks.

Recommended value:

X-Frame-Options: DENY

DENY prevents all framing. SAMEORIGIN allows framing by pages on the same origin. Avoid ALLOW-FROM as it has inconsistent browser support. For more flexible control, use the CSP frame-ancestors directive alongside this header.

Nginx: add_header X-Frame-Options "DENY" always;

Apache: Header always set X-Frame-Options "DENY"

Referrer-Policy

Controls how much referrer information is included in the Referer header when navigating away from your site. Prevents leaking sensitive URL paths, query parameters, and session tokens to third-party sites.

Recommended value:

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

This sends the full URL for same-origin requests, the origin only for cross-origin HTTPS requests, and nothing for HTTPS-to-HTTP transitions. Other useful values: no-referrer (maximum privacy), same-origin (referrer only for same-origin navigation).

Nginx: add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Apache: Header always set Referrer-Policy "strict-origin-when-cross-origin"

Permissions-Policy (formerly Feature-Policy)

Controls which browser features and APIs can be used on your page. Disabling unused features reduces your attack surface and prevents malicious scripts from accessing sensitive APIs.

Recommended value:

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

The empty parentheses () disable the feature entirely. Use (self) to allow the feature for your origin only. Available features include: accelerometer, autoplay, camera, clipboard-read, clipboard-write, fullscreen, geolocation, gyroscope, magnetometer, microphone, payment, usb, and more.

Nginx: add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;

Apache: Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"

Cross-Origin Headers

Cross-Origin-Embedder-Policy (COEP): require-corp ensures all cross-origin resources explicitly grant permission to be loaded. Required for enabling SharedArrayBuffer and high-resolution timers.

Cross-Origin-Opener-Policy (COOP): same-origin isolates your browsing context from cross-origin popups, preventing Spectre-class side-channel attacks.

Cross-Origin-Resource-Policy (CORP): same-origin prevents other sites from loading your resources, protecting against cross-origin data leaks.

Common Misconfigurations

Setting HSTS with a short max-age in production: A max-age of 300 seconds provides almost no protection. Use 31536000 (one year) after testing.

Using CSP with unsafe-inline and unsafe-eval: This effectively disables CSP's XSS protection. Migrate to nonce-based CSP.

Setting X-Frame-Options: ALLOW-FROM: This value has been removed from browser implementations. Use CSP frame-ancestors instead.

Forgetting the 'always' directive in Nginx: Without always, Nginx only adds headers on successful (2xx/3xx) responses. Error pages (4xx/5xx) will lack security headers, which is exactly when you need them most.

Not setting headers on all response types: Security headers must be set on HTML responses, API responses, and error pages. A missing header on a single endpoint can be exploited.

Complete Server Configuration

Nginx:

add_header Strict-Transport-Security "max-age=31536000; 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 "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';" always;

Apache (.htaccess):

Header always set Strict-Transport-Security "max-age=31536000; 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 "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';"

Testing Your Headers

Use curl -I https://yourdomain.com to inspect response headers from the command line. Online tools like securityheaders.com grade your configuration from A+ to F. Check headers on multiple paths including your homepage, API endpoints, and error pages. Verify headers after every deployment — configuration changes and reverse proxy updates can silently remove headers.

Frequently Asked Questions

What are the most important HTTP security headers?
The five essential security headers are Content-Security-Policy to prevent XSS, Strict-Transport-Security to enforce HTTPS, X-Content-Type-Options to prevent MIME sniffing, X-Frame-Options or frame-ancestors to prevent clickjacking, and Referrer-Policy to control information leakage. These five headers address the most common browser-side attack vectors.
What is HSTS preloading and should I use it?
HSTS preloading hardcodes your domain into the browser's HSTS list, so the browser uses HTTPS from the very first connection without ever making an initial HTTP request. To qualify, set max-age to at least one year, include includeSubDomains and preload directives, then submit to hstspreload.org. Warning: preloading is difficult to undo, so only enable it after confirming all subdomains support HTTPS.
Is X-Frame-Options still needed if I have CSP frame-ancestors?
CSP frame-ancestors supersedes X-Frame-Options and is more flexible. However, X-Frame-Options is still recommended as a fallback for older browsers that do not support CSP Level 2. Set both headers for maximum compatibility: X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none'.
What does X-Content-Type-Options: nosniff actually prevent?
It prevents the browser from MIME-type sniffing, which is when the browser ignores the declared Content-Type and guesses the file type based on content. Without this header, an attacker can upload a file with a .jpg extension containing JavaScript, and the browser may execute it as a script. The nosniff header forces the browser to respect the declared Content-Type.
What Referrer-Policy value should I use?
Use 'strict-origin-when-cross-origin' as the default. This sends the full URL for same-origin requests, the origin only for cross-origin HTTPS requests, and nothing when navigating from HTTPS to HTTP. Use 'no-referrer' if you need maximum privacy and do not rely on referrer information for analytics or attribution.

Written by Michael Lip — security tools at LochBot