HTTP Security Headers Configuration Guide
Security headers are your first line of defense against common web attacks like XSS, clickjacking, and MIME-type sniffing. This guide covers all essential headers with ready-to-use configurations.
Content-Security-Policy (CSP)
CSP is the most powerful security header, preventing XSS attacks by controlling which resources can be loaded. It tells browsers which sources are trusted for scripts, styles, images, and other content.
CSP Protects Against
- Cross-Site Scripting (XSS) attacks
- Data injection attacks
- Clickjacking (with frame-ancestors)
- Packet sniffing (with upgrade-insecure-requests)
Basic CSP Example
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.yourdomain.comCSP Directives Explained
| Directive | Controls | Example |
|---|---|---|
default-src | Fallback for all resources | 'self' |
script-src | JavaScript sources | 'self' https://cdn.example.com |
style-src | CSS sources | 'self' 'unsafe-inline' |
img-src | Image sources | 'self' data: https: |
connect-src | API/fetch destinations | 'self' https://api.example.com |
frame-ancestors | Who can embed this page | 'none' |
Strict-Transport-Security (HSTS)
HSTS forces browsers to only use HTTPS connections, preventing protocol downgrade attacks and cookie hijacking.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadHSTS Checklist
- Set max-age to at least 1 year (31536000 seconds)
- Include
includeSubDomainsif all subdomains use HTTPS - Add
preloadand submit to hstspreload.org - Ensure HTTPS works correctly before enabling
X-Frame-Options
Prevents your site from being embedded in iframes, protecting against clickjacking attacks.
# Prevent all framing
X-Frame-Options: DENY
# Allow framing from same origin only
X-Frame-Options: SAMEORIGINX-Content-Type-Options
Prevents browsers from MIME-sniffing, which can lead to XSS attacks.
X-Content-Type-Options: nosniffReferrer-Policy
Controls how much referrer information is sent with requests.
# Good balance of privacy and functionality
Referrer-Policy: strict-origin-when-cross-origin
# Maximum privacy
Referrer-Policy: no-referrerPermissions-Policy
Controls which browser features can be used (formerly Feature-Policy).
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()Complete Configurations
Nginx
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;Apache
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Content-Security-Policy "default-src 'self';"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>Express.js (Node.js)
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
},
frameguard: { action: 'sameorigin' },
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));