HTTP Security Headers: The Complete Implementation Guide
HTTP security headers are response headers that instruct browsers on how to handle your site's content. They are one of the most cost-effective security improvements you can make — a few lines of server configuration that mitigate entire classes of attacks like XSS, clickjacking, and protocol downgrade attacks. This guide covers every major security header with real configuration examples.
1. Strict-Transport-Security (HSTS)
HSTS tells browsers to always use HTTPS for your domain — even if the user types http://. This prevents SSL-stripping attacks, where an attacker downgrades your HTTPS connection to HTTP.
# Minimal (start here for testing) Strict-Transport-Security: max-age=300 # Production recommended Strict-Transport-Security: max-age=31536000; includeSubDomains # Maximum security (HSTS preload — irreversible without removal process) Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Applies HSTS to all subdomains. Only add if ALL subdomains support HTTPS.
Hardcodes your domain into browsers. Requires hstspreload.org submission. Very hard to undo.
2. Content-Security-Policy (CSP)
CSP is the most powerful security header, but also the most complex. It defines which sources are allowed to load scripts, styles, images, fonts, and other resources. A strict CSP prevents most XSS attacks — even if an attacker injects malicious code, the browser refuses to execute it if it violates the policy.
# Restrictive policy (start here, then loosen as needed) Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.trusted.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; # Report-only mode (test without breaking things) Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint
Start with Content-Security-Policy-Report-Only to identify violations without breaking your site. Use a service like Report URI to collect and analyse violation reports.
3. X-Frame-Options
Prevents your site from being embedded in <iframe>, <frame>, or <object> elements on other sites. This stops clickjacking attacks where an attacker overlays your site invisibly to trick users into clicking on hidden elements.
X-Frame-Options: DENY # Never allow framing X-Frame-Options: SAMEORIGIN # Allow framing only by same origin
Note: CSP's frame-ancestors directive is the modern replacement and provides more granular control. Set both for maximum compatibility.
4. X-Content-Type-Options
Prevents browsers from MIME-sniffing the content type of responses. Without this header, a browser might interpret a text file as JavaScript if it looks like script code — a vector for content injection attacks.
X-Content-Type-Options: nosniff
This is a zero-risk header — add it to every site unconditionally.
5. Referrer-Policy
Controls how much referrer information is sent when navigating from your site to external URLs. Without a policy, browsers may leak the full URL (including query parameters with tokens or search terms) in the Referer header.
# Recommended: send origin only for cross-origin requests Referrer-Policy: strict-origin-when-cross-origin # Maximum privacy: never send referrer information Referrer-Policy: no-referrer
6. Permissions-Policy
Controls which browser features and APIs your site can use — and prevents third-party iframes from accessing them. Replaces the older Feature-Policy header.
# Disable unnecessary browser APIs Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=(), payment=(self)
Complete Server Configuration Examples
# Nginx — complete security headers block
# In your server {} block:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" 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'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'self'; base-uri 'self';" always;# Next.js — next.config.js security headers
const securityHeaders = [
{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }];
},
};Security Header Grading Reference
| Header | Priority | Risk if Missing |
|---|---|---|
| Strict-Transport-Security | Critical | SSL stripping, protocol downgrade attacks |
| Content-Security-Policy | Critical | XSS, data injection attacks |
| X-Frame-Options | High | Clickjacking attacks |
| X-Content-Type-Options | High | MIME sniffing, drive-by downloads |
| Referrer-Policy | Medium | Information leakage to third parties |
| Permissions-Policy | Medium | Unauthorised API access from third-party frames |
Use our free Security Headers Checker to instantly analyse your site's HTTP security headers and get an A+ to F grade with specific recommendations for each missing header.
Common questions
Which HTTP security headers should every website have?
At minimum: Strict-Transport-Security (enforce HTTPS), Content-Security-Policy (prevent XSS), X-Frame-Options or CSP frame-ancestors (prevent clickjacking), X-Content-Type-Options: nosniff (prevent MIME sniffing), and Referrer-Policy (control referrer leakage). Permissions-Policy is also recommended to restrict browser feature access.
How do security headers affect website security?
Security headers give the browser explicit instructions about what is and is not allowed on your page. They stop the most common web attacks: CSP blocks XSS, X-Frame-Options stops clickjacking, HSTS prevents SSL stripping, and X-Content-Type-Options stops MIME sniffing. Each header addresses a specific attack class and they work together as layers of defence.
What is Content-Security-Policy Report-Only mode?
CSP Report-Only sends violation reports to a URL you specify without blocking any resources. This lets you test what a CSP policy would break before enforcing it. Use it to build your policy incrementally: start in report-only, identify violations, refine the policy, then switch to enforcement mode with the Content-Security-Policy header.
How do I add security headers on Vercel or Netlify?
On Vercel: add a headers array in vercel.json with source patterns and header name/value pairs — apply globally with source: "/(.*)" . On Netlify: add a [[headers]] section in netlify.toml or a _headers file in your publish directory. Both apply headers at the CDN edge without server configuration.
References and further reading
The specifications and primary documentation this article is based on.
Read next
More guides on the same subject.