Proper HTTP header configuration is the first line of defense against many website attacks. They protect users from XSS, clickjacking, man-in-the-middle, and other threats. This guide covers all key security headers with configurations for various servers.
Why Are Security Headers Important?
- Protect against XSS, clickjacking, MIME sniffing attacks
- Enforce HTTPS connections
- Control access to browser features
- Are checked by security audits
- Affect scores in tools like SecurityHeaders.com
1. Strict-Transport-Security (HSTS)
HSTS enforces HTTPS connections for all requests, eliminating man-in-the-middle attack possibilities.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Parameters
| Parameter | Meaning |
|---|---|
max-age | Validity time in seconds (31536000 = 1 year) |
includeSubDomains | Apply to all subdomains |
preload | Allow addition to browser preload lists |
HSTS Preload List
Adding to the HSTS Preload List means browsers will enforce HTTPS even on first visit. Requirements:
- Valid SSL certificate
- HTTP → HTTPS redirect
- HSTS with
max-ageminimum 1 year includeSubDomainsandpreloaddirectives
Note: HSTS is irreversible for the max-age duration. Start with a short period (e.g., 300 seconds) and gradually increase.
2. Content-Security-Policy (CSP)
CSP defines allowed sources for scripts, styles, images, and other resources. It’s the most important XSS protection header.
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'
Main Directives
| Directive | Controls |
|---|---|
default-src | Default source for all types |
script-src | JavaScript scripts |
style-src | CSS styles |
img-src | Images |
font-src | Fonts |
connect-src | XHR, fetch, WebSocket |
frame-src | iframe sources |
frame-ancestors | Who can embed page in iframe |
Report-Only Mode
Test CSP without blocking:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
Detailed guide: CSP for Google Services
3. X-Frame-Options
X-Frame-Options protects against clickjacking attacks by controlling whether a page can be embedded in an iframe.
X-Frame-Options: SAMEORIGIN
Values
| Value | Meaning |
|---|---|
DENY | Never allow iframe embedding |
SAMEORIGIN | Only from same domain |
ALLOW-FROM uri | Only from specified source (deprecated) |
Note: CSP frame-ancestors is the newer alternative. Use both for backward compatibility.
4. X-Content-Type-Options
X-Content-Type-Options prevents “MIME sniffing” - browser attempts to guess file type.
X-Content-Type-Options: nosniff
Without this header, the browser may interpret a text file as JavaScript, enabling XSS attacks.
5. Referrer-Policy
Referrer-Policy controls what source information is sent during navigation.
Referrer-Policy: strict-origin-when-cross-origin
Values
| Value | Meaning |
|---|---|
no-referrer | Never send referrer |
same-origin | Only for same domain |
strict-origin | Domain only, not on HTTPS→HTTP downgrade |
strict-origin-when-cross-origin | Full URL for same-origin, domain for cross-origin (recommended) |
6. Permissions-Policy
Permissions-Policy (formerly Feature-Policy) controls access to browser features like geolocation, camera, or microphone.
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
Syntax
()- blocked for all(self)- only this domain*- allowed for all
Popular Features
| Feature | Controls |
|---|---|
geolocation | Geolocation API |
microphone | Microphone access |
camera | Camera access |
payment | Payment Request API |
fullscreen | Fullscreen API |
7. X-XSS-Protection (Deprecated)
X-XSS-Protection activated the built-in XSS filter in older browsers. It is deprecated - modern browsers (Chrome 78+) removed this filter.
X-XSS-Protection: 0
Why 0? The value 1; mode=block can cause side-channel attacks. Use CSP instead.
Complete Configuration
Apache (.htaccess)
<IfModule mod_headers.c>
# HSTS - enforce HTTPS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# CSP - customize to your needs
Header always set 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'"
# Clickjacking protection
Header always set X-Frame-Options "SAMEORIGIN"
# Prevent MIME sniffing
Header always set X-Content-Type-Options "nosniff"
# Referrer control
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Browser API restrictions
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Disable deprecated XSS filter
Header always set X-XSS-Protection "0"
</IfModule>
Nginx
server {
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'" always;
# Clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
# Referrer
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# XSS filter disabled
add_header X-XSS-Protection "0" always;
}
Node.js (Express + Helmet)
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
frameAncestors: ["'self'"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
frameguard: { action: 'sameorigin' },
noSniff: true,
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
xssFilter: false
}));
Netlify (netlify.toml)
[[headers]]
for = "/*"
[headers.values]
Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
X-Frame-Options = "SAMEORIGIN"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "geolocation=(), microphone=(), camera=()"
Vercel (vercel.json)
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
]
}
]
}
Testing Headers
SecurityHeaders.com
https://securityheaders.com/ - quick scan with A-F grade.
Observatory by Mozilla
https://observatory.mozilla.org/ - comprehensive security audit.
Chrome DevTools
- Open DevTools (F12)
- Network tab
- Select HTML document
- Check Response Headers
curl
curl -I https://example.com
Security Checklist
Critical
- HSTS - enforce HTTPS
- X-Frame-Options - clickjacking protection
- X-Content-Type-Options: nosniff
Recommended
- CSP - XSS protection
- Referrer-Policy - source information control
- Permissions-Policy - browser API restrictions
Deprecated (disable)
- X-XSS-Protection: 0 - disable legacy filter
Summary
Security header configuration is a simple step that significantly increases site protection:
- HSTS enforces HTTPS and protects against downgrade attacks
- CSP is the most powerful XSS protection
- X-Frame-Options blocks clickjacking
- Test regularly on SecurityHeaders.com and Observatory
Well-configured headers can prevent many attacks with minimal effort.
Sources
-
MDN - HTTP Security Headers https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security
-
OWASP - Security Headers https://owasp.org/www-project-secure-headers/
-
SecurityHeaders.com https://securityheaders.com/
-
Mozilla Observatory https://observatory.mozilla.org/
-
HSTS Preload List https://hstspreload.org/

