HTTP Security Headers Checklist: The Complete Guide (2025)
A complete checklist of HTTP security headers every web app should set. Covers CSP, HSTS, X-Frame-Options, CORP, and more — with copy-paste examples for Next.js, nginx, and Express.
HTTP security headers are directives sent by your web server that tell browsers how to behave when handling your site's content. Correctly setting them closes a wide range of attack vectors — XSS, clickjacking, MIME sniffing, cross-origin attacks — with just a few lines of configuration.
This checklist covers every header that matters, what each one does, and how to configure it.
The Critical Headers
1. Content-Security-Policy (CSP)
Prevents: Cross-site scripting (XSS), data injection attacks, clickjacking
CSP is the most powerful security header. It defines which sources the browser is allowed to load scripts, styles, images, fonts, and other resources from.
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{RANDOM}'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
Key directives:
| Directive | Controls |
|---|---|
default-src | Fallback for all resource types |
script-src | JavaScript sources |
style-src | CSS sources |
img-src | Image sources |
connect-src | fetch(), XHR, WebSocket destinations |
frame-ancestors | Who can embed this page (replaces X-Frame-Options) |
form-action | Where forms can submit |
base-uri | Allowed base URL values |
Avoid 'unsafe-inline' for scripts. Use nonces or hashes instead.
2. Strict-Transport-Security (HSTS)
Prevents: Protocol downgrade attacks, SSL stripping, man-in-the-middle on HTTP
HSTS tells browsers to only connect to your site over HTTPS, even if the user types http:// or clicks an HTTP link.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000— Cache for 1 year (minimum for Google preload list)includeSubDomains— Also apply to all subdomainspreload— Submit to browser preload lists (hardcodes HTTPS-only in Chrome/Firefox)
Important: Only add preload when you're certain all subdomains serve HTTPS. Removing from the preload list can take months.
3. X-Content-Type-Options
Prevents: MIME type sniffing attacks
X-Content-Type-Options: nosniff
Tells the browser to respect the declared Content-Type and not try to guess what a file is. Without this, a browser might execute a text file as JavaScript if the server sends it with an ambiguous content type.
This is a single value with no configuration — just add it.
4. X-Frame-Options
Prevents: Clickjacking attacks
X-Frame-Options: DENY
Or, if you need to allow embedding within your own domain:
X-Frame-Options: SAMEORIGIN
Note: X-Frame-Options is superseded by CSP's frame-ancestors directive. If you have CSP, use frame-ancestors 'none' or frame-ancestors 'self' there instead. Keep X-Frame-Options for older browser compatibility.
5. Referrer-Policy
Prevents: Leaking sensitive URL paths to third parties
Controls how much referrer information is included with requests when a user navigates away from your site.
Referrer-Policy: strict-origin-when-cross-origin
Policy options:
| Value | When | What's sent |
|---|---|---|
no-referrer | Always | Nothing |
strict-origin | Cross-origin | Origin only |
strict-origin-when-cross-origin | Same-origin: full URL; Cross-origin: origin only | Recommended |
unsafe-url | Always | Full URL (dangerous) |
Avoid unsafe-url — it leaks session tokens, password reset tokens, and other sensitive URL parameters to third-party analytics and CDNs.
6. Permissions-Policy
Prevents: Unauthorized access to browser APIs (camera, mic, location, etc.)
Formerly Feature-Policy. Restricts which browser features your site can use, and disables features you don't need entirely.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), autoplay=(self)
Empty parentheses () disable the feature for all origins. Use (self) to allow for your own origin.
7. Cross-Origin-Opener-Policy (COOP)
Prevents: Cross-origin window hijacking, Spectre side-channel attacks
Cross-Origin-Opener-Policy: same-origin
Isolates your browsing context from cross-origin popups, preventing them from accessing your window object and enabling SharedArrayBuffer.
8. Cross-Origin-Resource-Policy (CORP)
Prevents: Cross-origin reads of your resources
Cross-Origin-Resource-Policy: same-origin
Prevents other origins from loading your resources (images, scripts, etc.) using no-CORS requests. Use same-site if you have same-site cross-origin subdomains that need to load your resources.
9. Cross-Origin-Embedder-Policy (COEP)
Required alongside COOP to enable cross-origin isolation (needed for SharedArrayBuffer, high-resolution timers):
Cross-Origin-Embedder-Policy: require-corp
Only enable if all your third-party resources support CORP headers.
Headers to Remove
Some headers leak information about your server and should be removed:
# Remove these:
Server: Apache/2.4.52 (Ubuntu) # Remove — reveals server software + version
X-Powered-By: Express # Remove — reveals framework
X-AspNet-Version: 4.0.30319 # Remove — reveals .NET version
Implementation Examples
Next.js (next.config.ts)
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin' },
{ key: 'Cross-Origin-Resource-Policy', value: 'same-origin' },
],
},
];
},
};
nginx
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 Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
Express (Node.js)
Use the helmet package, which sets most of these automatically:
import helmet from 'helmet';
app.use(helmet());
Priority Order
If you're starting from scratch, implement in this order:
X-Content-Type-Options: nosniff— 30 seconds, zero riskX-Frame-Options: DENY— 30 seconds, zero riskReferrer-Policy: strict-origin-when-cross-origin— 30 secondsStrict-Transport-Security— 5 minutes (test on staging first)Permissions-Policy— 5 minutes, tailor to your featuresContent-Security-Policy— Hours to days (requires auditing all resources)Cross-Origin-*headers — After CSP is working
CSP is deliberately last because it requires inventorying every resource your page loads and breaks easily if misconfigured. Start with Content-Security-Policy-Report-Only to monitor without enforcement.