HTTP security headers are your first line of defense against common web attacks. These headers instruct browsers on how to handle your content, preventing XSS, clickjacking, and other vulnerabilities. Most can be implemented in minutes but provide lasting protection. This guide explains each essential header and provides ready-to-use configurations.
- Security headers protect against XSS, clickjacking, and MIME sniffing attacks
- Content-Security-Policy is powerful but requires careful configuration
- Most headers can be added with a single line in your server config
- Test thoroughly after implementation—strict policies can break functionality
I. Content-Security-Policy (CSP)
The most powerful security header—controls what resources browsers can load.
A. What CSP Prevents
- XSS attacks: Blocks execution of injected malicious scripts.
- Data injection: Prevents unauthorized content from being embedded.
- Click-hijacking: Controls framing of your pages.
B. Basic CSP Structure
# Nginx
add_header Content-Security-Policy "directive value; directive value;";
# Example: Allow only same-origin content
add_header Content-Security-Policy "default-src 'self';";
C. Common Directives
- default-src: Fallback for unspecified directives.
- script-src: Controls JavaScript sources.
- style-src: Controls CSS sources.
- img-src: Controls image sources.
- connect-src: Controls AJAX, WebSocket, fetch destinations.
- frame-src: Controls iframe sources.
- font-src: Controls web font sources.
D. Practical CSP Example
# Nginx - Typical WordPress/blog CSP
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'unsafe-inline' https://www.google-analytics.com https://www.googletagmanager.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://www.google-analytics.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://www.google-analytics.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
";
E. CSP Report-Only Mode
# Test CSP without enforcing (see what would be blocked)
add_header Content-Security-Policy-Report-Only "
default-src 'self';
report-uri /csp-report-endpoint;
";
# Review reports before switching to enforced CSP
II. Strict-Transport-Security (HSTS)
Forces browsers to always use HTTPS for your domain.
A. Basic HSTS
# Nginx - Force HTTPS for 1 year
add_header Strict-Transport-Security "max-age=31536000" always;
B. HSTS with Subdomains
# Include all subdomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
C. HSTS Preload
# Request inclusion in browser preload list
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# After adding this, submit your domain to:
# https://hstspreload.org/
HSTS preload is difficult to undo. Ensure ALL subdomains support HTTPS before enabling preload.
III. X-Frame-Options
Prevents your site from being loaded in frames on other sites (clickjacking protection).
A. Options
# Block all framing
add_header X-Frame-Options "DENY" always;
# Allow only same origin
add_header X-Frame-Options "SAMEORIGIN" always;
# Allow specific domain (deprecated, use CSP frame-ancestors instead)
add_header X-Frame-Options "ALLOW-FROM https://trusted-site.com" always;
B. Modern Alternative: CSP frame-ancestors
# More flexible than X-Frame-Options
add_header Content-Security-Policy "frame-ancestors 'none'";
# or
add_header Content-Security-Policy "frame-ancestors 'self'";
# or
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted-site.com";
IV. X-Content-Type-Options
Prevents MIME type sniffing attacks.
A. Implementation
# Nginx - Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
B. Why It Matters
- Attack vector: Attackers upload files with wrong extensions.
- Browser behavior: Without nosniff, browsers may "guess" content type.
- Risk: Image file executed as JavaScript if MIME sniffed.
V. X-XSS-Protection
Enables browser's built-in XSS filter (legacy, but still useful for older browsers).
A. Recommended Setting
# Enable XSS filter, block page if attack detected
add_header X-XSS-Protection "1; mode=block" always;
B. Modern Note
- Chrome removed: XSS Auditor removed in Chrome 78+.
- Still useful: Protects users on older browsers.
- CSP better: Content-Security-Policy provides superior XSS protection.
VI. Referrer-Policy
Controls how much referrer information is sent with requests.
A. Common Policies
# Send full URL only to same origin, origin-only to cross-origin
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Never send referrer
add_header Referrer-Policy "no-referrer" always;
# Send only origin (no path)
add_header Referrer-Policy "origin" always;
B. Privacy Considerations
- URLs can leak data: Query strings may contain tokens or user info.
- Analytics impact: Some policies break referrer tracking.
- Recommended:
strict-origin-when-cross-originbalances privacy and functionality.
VII. Permissions-Policy
Controls which browser features can be used (formerly Feature-Policy).
A. Common Restrictions
# Disable features you don't use
add_header Permissions-Policy "
accelerometer=(),
camera=(),
geolocation=(),
gyroscope=(),
magnetometer=(),
microphone=(),
payment=(),
usb=()
" always;
B. Allowing Specific Origins
# Allow geolocation only from self
add_header Permissions-Policy "geolocation=(self)" always;
# Allow camera from specific origin
add_header Permissions-Policy "camera=(self https://video-chat.example.com)" always;
VIII. Complete Nginx Configuration
# /etc/nginx/snippets/security-headers.conf
# HSTS - Force HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS Protection (legacy browsers)
add_header X-XSS-Protection "1; mode=block" always;
# Referrer Policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions Policy
add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
# Content Security Policy (adjust for your needs)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; frame-ancestors 'self';" always;
# In your server block:
# include snippets/security-headers.conf;
IX. Apache Configuration
# .htaccess or httpd.conf
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
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 Permissions-Policy "accelerometer=(), camera=(), geolocation=()"
Header always set Content-Security-Policy "default-src 'self';"
</IfModule>
X. Testing Your Headers
A. Online Tools
- Security Headers: securityheaders.com - Grades your implementation.
- Mozilla Observatory: observatory.mozilla.org - Comprehensive security scan.
- CSP Evaluator: csp-evaluator.withgoogle.com - Tests CSP strength.
B. Command Line Check
# Check all response headers
curl -I https://example.com
# Check specific header
curl -sI https://example.com | grep -i "content-security-policy"
XI. Conclusion
HTTP security headers provide crucial protection with minimal implementation effort. Start with the basics—HSTS, X-Frame-Options, X-Content-Type-Options—then work toward a comprehensive Content-Security-Policy. Always test in report-only mode first, and use tools like Security Headers to verify your implementation. These invisible safeguards significantly raise the bar for attackers attempting to compromise your site or your visitors.
Which security headers have you implemented? Share your configuration tips in the comments!