The Invisible Shield: HTTP Security Headers
HTTP security headers are response headers a web server sends alongside a page to tell the browser how to handle that content safely — and because the browser enforces them, they keep protecting your users even when your application code has a bug. A handful of headers block the most common web attacks: Content-Security-Policy stops cross-site scripting (XSS), Strict-Transport-Security forces HTTPS to defeat downgrade and man-in-the-middle attacks, X-Frame-Options and CSP frame-ancestors stop clickjacking, X-Content-Type-Options stops MIME-sniffing, and Referrer-Policy and Permissions-Policy limit what leaks out and which browser features a page can reach.
That is the summary an AI overview will give you. What it can't give you is the part that actually matters when you sit down to configure a server: which headers to turn on first, which ones will break your site if you deploy them wrong, exactly what value to send, and how the newer cross-origin isolation headers (COOP/COEP/CORP) fit in. This guide is the full header-by-header reference — with a decision table, real config for Nginx, Apache, and Next.js, and the pitfalls that turn a security upgrade into an outage. It's the pillar for our security-header cluster; each header below links to a deep dive.
Despite their importance, security headers remain one of the most overlooked parts of web security — large-scale scans routinely find that the majority of sites are missing basics like HSTS and a real CSP. The good news is that they are cheap to add and, added in the right order, low-risk. Below is the complete map before we go header by header.
Every security header at a glance
Use this as the decision table. The four headers marked "safe to add first" almost never break a working site; CSP is the most powerful but needs a staged rollout.
| Header | What it stops | Example value | Notes |
|---|---|---|---|
| Strict-Transport-Security (HSTS) | Protocol downgrade / SSL-strip MITM | max-age=31536000; includeSubDomains; preload | Safe to add first. Consider the HSTS preload list. |
| X-Content-Type-Options | MIME-type sniffing → files run as scripts | nosniff | Safe to add first. Only one valid value. |
| X-Frame-Options | Clickjacking (framing) | DENY or SAMEORIGIN | Safe to add first. Pair with frame-ancestors; see X-Frame-Options vs frame-ancestors. |
| Referrer-Policy | Referrer URL leakage to third parties | strict-origin-when-cross-origin | Safe to add first. Balances privacy with analytics. |
| Content-Security-Policy (CSP) | XSS, injection, unwanted resource loads | default-src 'self'; script-src 'self' | Most powerful, highest breakage risk. Roll out with report-only mode and handle third-party resources. |
| Permissions-Policy | Abuse of camera, mic, geolocation, etc. | geolocation=(), camera=(), microphone=() | Deny features you don't use. See Permissions-Policy explained. |
| Cross-Origin-Opener-Policy (COOP) | Cross-window attacks (XS-Leaks) | same-origin | Part of cross-origin isolation. |
| Cross-Origin-Embedder-Policy (COEP) | Loading un-vetted cross-origin resources | require-corp | With COOP, enables crossOriginIsolated. |
| Cross-Origin-Resource-Policy (CORP) | Side-channel (Spectre) resource theft | same-origin | Declares who may embed a resource. |
Despite their critical importance, security headers remain one of the most overlooked aspects of web security. Studies consistently show that most websites fail to implement basic security headers, leaving users vulnerable to attacks that proper headers would prevent. Understanding and implementing these headers is essential for any website owner or developer concerned with security.
How HTTP Headers Work
Every time a browser requests a web page, the server responds with the requested content plus HTTP headers—metadata about the response:
Standard Response Headers
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
Server: nginx
These headers tell the browser what type of content it's receiving and how to process it.
Security Response Headers
Security headers add protective instructions:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
These headers tell the browser:
- Always use HTTPS for this site
- Only load scripts from the same origin
- Don't allow this page to be framed
- Don't try to guess content types
Browsers that understand these headers enforce the policies, providing protection even if attackers find application vulnerabilities.
Critical Security Headers for 2025
1. Content-Security-Policy (CSP)
Purpose: Prevents XSS attacks by controlling which resources can load
How it works: Defines approved sources for scripts, styles, images, and other resources. Browsers refuse to load content from unapproved sources.
Example:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'
This policy:
- Allows resources only from the same origin by default
- Permits scripts from same origin and cdn.example.com
- Allows inline styles (though this weakens protection)
2025 Best Practice: Use nonce-based CSP for dynamic content or hash-based CSP for static sites rather than 'unsafe-inline' which significantly weakens protection.
2. Strict-Transport-Security (HSTS)
Purpose: Forces browsers to always use HTTPS, preventing downgrade attacks
How it works: Once browsers receive this header, they refuse to connect over HTTP for the specified duration, even if users type "http://" in the address bar.
Example:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
This tells browsers:
- Enforce HTTPS for 31,536,000 seconds (1 year)
- Apply to all subdomains
- Include this site in the HSTS preload list
Critical: Submit your domain to the HSTS preload list for maximum protection. Preloading ensures browsers enforce HTTPS before the very first connection, eliminating the vulnerability window.
3. X-Frame-Options / frame-ancestors (CSP)
Purpose: Prevents clickjacking attacks by controlling whether pages can be embedded in frames
How it works: Tells browsers whether to allow the page to be displayed in <iframe>, <frame>, <embed>, or <object> elements.
X-Frame-Options example:
X-Frame-Options: DENY
Options:
DENY: Never allow framingSAMEORIGIN: Allow framing only by same originALLOW-FROM https://example.com: Allow specific origin (deprecated)
CSP frame-ancestors example:
Content-Security-Policy: frame-ancestors 'none'
Best Practice: Use both X-Frame-Options and CSP frame-ancestors for maximum compatibility. Modern browsers prefer frame-ancestors, but older browsers need X-Frame-Options. For a side-by-side breakdown of the two, read X-Frame-Options vs CSP frame-ancestors.
4. X-Content-Type-Options
Purpose: Prevents MIME-type sniffing attacks
How it works: Stops browsers from trying to "guess" content types, forcing them to respect the Content-Type header.
Example:
X-Content-Type-Options: nosniff
Why it matters: Without this header, browsers might interpret uploaded files as executable scripts even if the server sends them as images, creating XSS vulnerabilities.
5. Referrer-Policy
Purpose: Controls how much referrer information browsers send when navigating from your site
How it works: Determines what information the Referer header includes when users click links or load resources.
Example:
Referrer-Policy: strict-origin-when-cross-origin
Options range from:
no-referrer: Never send referer informationstrict-origin-when-cross-origin: Send full URL for same-origin, only origin for cross-origin HTTPS, nothing for HTTPunsafe-url: Send full URL always (not recommended)
2025 Best Practice: Use strict-origin-when-cross-origin or no-referrer-when-downgrade to balance privacy with legitimate analytics needs.
6. Permissions-Policy (formerly Feature-Policy)
Purpose: Controls which browser features and APIs the site can use
How it works: Allows or denies access to features like geolocation, camera, microphone, etc.
Example:
Permissions-Policy: geolocation=(), microphone=(), camera=()
This denies access to geolocation, microphone, and camera for all origins, preventing malicious scripts from accessing these features. For the full syntax and how it differs from the old Feature-Policy header, see what Permissions-Policy is and how it enhances security.
7. Cross-Origin Isolation: COOP, COEP, and CORP
Purpose: Defend against side-channel attacks (such as Spectre) and cross-window information leaks, and — as a bonus — unlock powerful browser APIs.
These three newer headers work together to control how your page interacts with content from other origins. They matter most for sites handling sensitive data or those that need high-precision timers and SharedArrayBuffer.
Cross-Origin-Opener-Policy (COOP) severs the connection between your page and any window that opened it (or that it opens via window.open), so another origin can't reach into your page's window object:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy (COEP) requires every cross-origin resource your page loads to explicitly opt in (via CORP or CORS) before the browser will fetch it:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy (CORP) goes on your resources (images, scripts, fonts) to declare who is allowed to embed them, blocking side-channel theft:
Cross-Origin-Resource-Policy: same-origin
Why it matters: Sending Cross-Origin-Opener-Policy: same-origin together with Cross-Origin-Embedder-Policy: require-corp puts the page into a cross-origin isolated state (self.crossOriginIsolated === true). This is what re-enables SharedArrayBuffer and high-resolution timers that browsers disabled after Spectre. The trade-off: require-corp can break third-party embeds (ads, widgets, images) that don't send the right CORP or CORS headers, so test carefully before enforcing it.
Deprecated Headers to Avoid
Several older headers are now deprecated and can actually create vulnerabilities:
X-XSS-Protection
Status: Deprecated, do NOT use
Why: This header enabled browsers' built-in XSS filters, but research showed these filters could be exploited to create vulnerabilities rather than prevent them.
Instead: Use Content-Security-Policy which provides far better XSS protection
Public-Key-Pins (HPKP)
Status: Deprecated
Why: Certificate pinning could permanently lock users out of sites if keys were lost
Instead: Use Certificate Transparency monitoring
How Security Headers Protect Against Common Attacks
Cross-Site Scripting (XSS)
Attack: Injecting malicious scripts into websites to steal data or hijack sessions
Protection: Content-Security-Policy prevents execution of unauthorized scripts by whitelisting approved sources and blocking inline scripts
Example: Even if an attacker injects <script>alert('hacked')</script> into a page, CSP refuses to execute it
Clickjacking
Attack: Tricking users into clicking invisible iframes that perform unintended actions
Protection: X-Frame-Options and frame-ancestors prevent malicious sites from embedding your pages in invisible iframes
Example: Attacker can't overlay your banking site's "Transfer Money" button over a fake game to trick users into authorizing transfers
Man-in-the-Middle (MITM) Attacks
Attack: Intercepting communication between browser and server to steal or modify data
Protection: HSTS forces HTTPS, preventing attackers from downgrading connections to unencrypted HTTP
Example: Public WiFi attackers can't intercept your HSTS-protected site's traffic by redirecting to HTTP
Code Injection
Attack: Uploading malicious files that browsers execute as scripts
Protection: X-Content-Type-Options prevents MIME-type confusion, ensuring uploaded images aren't executed as JavaScript
Example: User uploads "image.jpg" containing JavaScript; nosniff prevents browser from executing it
Implementing Security Headers
Web Server Configuration
Headers are typically set in web server configuration:
Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
Apache:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Content-Security-Policy "default-src 'self'"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Application-Level Headers
Many frameworks allow setting headers in application code:
Express.js:
const helmet = require('helmet');
app.use(helmet());
Next.js:
// next.config.js
module.exports = {
async headers() {
return [{
source: '/:path*',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains'
}
]
}]
}
}
Cloud Provider Settings
Many hosting platforms provide security header configuration:
Cloudflare: Transform Rules and Page Rules AWS CloudFront: Lambda@Edge or CloudFront Functions Vercel: vercel.json headers configuration Netlify: _headers file or netlify.toml
Testing and Validation
After implementing security headers, validate the configuration. Scan your live site right here — the analyzer fetches your headers and grades each one with specific fixes:
Online Scanners
Security Headers: securityheaders.com provides grades (A+ to F) Mozilla Observatory: observatory.mozilla.org comprehensive scanning Our tool: Security Headers Analyzer
Browser Developer Tools
Check actual headers received:
- Open Developer Tools (F12)
- Navigate to Network tab
- Reload page
- Click on the main document request
- View Response Headers
Verify all expected security headers are present with correct values.
CSP Reporting
Implement CSP reporting to monitor violations:
Content-Security-Policy: default-src 'self'; report-uri /csp-report
This sends reports of blocked resources to your endpoint, helping identify issues before enforcing stricter policies.
Common Implementation Mistakes
Starting Too Strict
Mistake: Implementing highly restrictive CSP immediately, breaking functionality
Solution: Start with report-only mode, monitor reports, gradually tighten policy:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
This is the single most important CSP habit — see what CSP report-only mode is for the full staged-rollout workflow, and how to handle CSP for third-party resources when analytics, ads, or embeds get blocked.
Inconsistent Headers
Mistake: Headers set on some responses but not others
Solution: Configure at web server level to ensure all responses include headers
Allowing 'unsafe-inline'
Mistake: CSP with 'unsafe-inline' to avoid refactoring code
Solution: Use nonces or hashes for legitimate inline scripts/styles
Forgetting Subdomains
Mistake: HSTS without includeSubDomains, leaving subdomains vulnerable
Solution: Include includeSubDomains directive and test all subdomains
Conclusion
HTTP security headers provide crucial protection against common web attacks including XSS, clickjacking, code injection, and man-in-the-middle attacks. These headers act as an additional defense layer that browsers enforce, protecting users even when application code has vulnerabilities.
Every website should implement at minimum: Content-Security-Policy (to prevent XSS), Strict-Transport-Security (to enforce HTTPS), X-Frame-Options/frame-ancestors (to prevent clickjacking), X-Content-Type-Options (to prevent MIME confusion), and Referrer-Policy (for privacy).
Start with basic implementations, test thoroughly, monitor for issues, and progressively strengthen policies. Avoid deprecated headers like X-XSS-Protection that can create vulnerabilities.
Proper security headers are no longer optional—they're essential for protecting your users and your website from modern threats.
Want to check your website's security headers? Try our free Security Headers Analyzer to scan your site and receive a detailed grade with specific recommendations for improvement.