Security Headers
HTTP security headers provide an extra layer of security by helping to detect and mitigate certain types of attacks, including XSS and data injection.
Easy Wins
Recommended Headers
Each header below addresses specific attack vectors. Implementing all of them provides comprehensive browser-level protection.
All applications should implement these headers:
# Prevent clickjacking
X-Frame-Options: DENY
# Enable browser XSS filter
X-XSS-Protection: 1; mode=block
# Prevent MIME-type sniffing
X-Content-Type-Options: nosniff
# Force HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Control referrer information
Referrer-Policy: strict-origin-when-cross-origin
# Permissions Policy (formerly Feature-Policy)
Permissions-Policy: geolocation=(), microphone=(), camera=()
# Content Security Policy
Content-Security-Policy: default-src 'self'X-Frame-Options
Prevents your site from being embedded in iframes on other domains, blocking clickjacking attacks where attackers overlay invisible frames to hijack clicks.
X-Content-Type-Options
Stops browsers from guessing ("sniffing") content types. Prevents attacks where a file served as text/plain is executed as JavaScript because the browser detected script content.
Strict-Transport-Security (HSTS)
Forces browsers to only connect via HTTPS for the specified duration. Prevents SSL stripping attacks and accidental HTTP connections. Add preload for browser built-in list.
Referrer-Policy
Controls how much referrer information is sent with requests. strict-origin-when-cross-origin sends full path to same-origin, only origin to cross-origin, nothing to less secure.
Permissions-Policy
Controls which browser features (camera, microphone, geolocation) your site can use. Empty parentheses () disable the feature entirely. Limits damage if XSS occurs.
Content-Security-Policy
The most powerful security header. Defines allowed sources for scripts, styles, images, etc. default-src 'self' only allows resources from your own domain.
Implementation
Here's how to implement these headers in popular frameworks and web servers. Most provide middleware or configuration options to set headers globally.
Express.js (Node.js)
Using helmet:
const helmet = require('helmet');
app.use(helmet()); // Enables most security headers
// Or configure individually:
app.use(helmet.frameguard({ action: 'deny' }));
app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));
app.use(helmet.noSniff());
app.use(helmet.xssFilter());Flask (Python)
Using flask-talisman:
from flask_talisman import Talisman
Talisman(app,
force_https=True,
strict_transport_security=True,
strict_transport_security_max_age=31536000,
content_security_policy=csp
)Django
In settings.py:
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = TrueWeb Server Config
Apache (.htaccess):
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"Nginx:
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;๐งช Testing Verification
Use these commands and tools to verify your security headers are properly configured.
Command Line Tests
# Check all security headers at once
curl -I https://yoursite.com | grep -iE "x-frame|x-content|strict-transport|referrer|content-security|permissions"
# Expected output should include:
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# Strict-Transport-Security: max-age=31536000; includeSubDomains
# Referrer-Policy: strict-origin-when-cross-origin
# Content-Security-Policy: default-src 'self'Online Testing Tools
securityheaders.com
Comprehensive header analysis with grading
observatory.mozilla.org
Mozilla's security scanner with recommendations
csp-evaluator.withgoogle.com
Google's CSP analysis tool
hstspreload.org
Check HSTS preload list eligibility
โ ๏ธ Common Mistakes
โ Missing "always" in Nginx
# DON'T: Headers only on 2xx responses
add_header X-Frame-Options "DENY";Without "always", headers missing on error pages
โ Correct Approach
# DO: Include "always" for all responses
add_header X-Frame-Options "DENY" always;Headers sent on all response codes
โ HSTS Without HTTPS
# DON'T: Enable HSTS before HTTPS works
# If HTTPS breaks, users can't access site!Test HTTPS thoroughly before enabling HSTS
โ Correct Approach
# DO: Start with short max-age, then increase
max-age=300 # 5 min (testing)
max-age=31536000 # 1 year (production)Gradually increase HSTS duration
โ Headers Set in Wrong Location
# DON'T: Set in location block only
location /api {
add_header X-Frame-Options "DENY";
}
# Other locations unprotected!Headers not applied to all routes
โ Correct Approach
# DO: Set in server block for all routes
server {
add_header X-Frame-Options "DENY" always;
# Applies to all locations
}Headers applied globally