How to Implement Security Headers: Quick Guide
If your security scan found missing or misconfigured security headers, this guide shows you how to implement them quickly. Most headers take 5-30 minutes to configure and provide immediate protection.
Quick Fix: Essential Security Headers
These three headers provide the most protection for the least effort. Start here:
1. X-Content-Type-Options (5 minutes) Prevents browsers from guessing content types, which can lead to XSS attacks.
2. X-Frame-Options (15 minutes) Prevents clickjacking by blocking your site from being embedded in frames.
3. HSTS (30 minutes) Forces browsers to use HTTPS, preventing downgrade attacks.
For a complete guide to all security headers, see our Security Headers Guide.
Implementation by Platform
Nginx
Add these headers to your HTTPS server block:
server {
listen 443 ssl;
server_name example.com;
# Essential headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Additional recommended headers
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
Important: Use always so headers are sent even on error responses.
Apache
Enable mod_headers first, then add to your VirtualHost:
<VirtualHost *:443>
ServerName example.com
# Essential headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Additional recommended headers
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</VirtualHost>
Note: Use always to ensure headers are sent on all responses, including errors.
Node.js (Express)
Use the helmet package for security headers:
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet({
contentSecurityPolicy: false, // Configure CSP separately if needed
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
Helmet sets most headers by default. Customize as needed.
Python (Django)
Add to your settings.py:
# Essential security headers
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
Next.js
Add to next.config.js:
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
}
]
}
]
}
}
Content Security Policy (CSP)
CSP is more complex and requires careful configuration. Start with a report-only policy:
# Phase 1: Report-only (doesn't break anything)
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report;" always;
# Phase 2: After reviewing reports, enable enforcement
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';" always;
Tip: Use CSP Report-Only first to see what breaks, then gradually tighten the policy.
Verification
After implementing headers, verify they're working:
Command Line:
curl -I https://example.com | grep -i "strict-transport-security\|x-frame-options\|x-content-type-options"
Browser:
- Open Developer Tools (F12)
- Go to Network tab
- Reload the page
- Click on the main document request
- Check Response Headers section
Online Tools:
- Barrion - Comprehensive header analysis and continuous monitoring
- Mozilla Observatory - Security header scoring
Common Issues
Headers not appearing:
- Make sure you're testing on HTTPS (some headers require HTTPS)
- Check that
alwaysis used in Nginx/Apache - Verify server configuration is loaded (restart server)
Breaking functionality:
- Start with report-only CSP to see violations
- Gradually tighten policies instead of going strict immediately
- Check browser console for CSP violations
Nginx headers only on 200 responses:
- Add
alwayskeyword:add_header X-Frame-Options "DENY" always; - Without
always, headers are only sent on 200, 201, 204, 206, 301, 302, 303, 304, 307, or 308 responses
Next Steps
Once you've implemented the essential headers:
- Test thoroughly - Make sure nothing broke
- Monitor for violations - Watch browser console and CSP reports
- Add CSP gradually - Start report-only, then enforce
- Implement remaining headers - Referrer-Policy, Permissions-Policy, etc.
- Set up monitoring - Use tools like Barrion to track header changes
For detailed information on each header, implementation strategies, and troubleshooting, see our complete Security Headers Guide.