HTTP Security Headers: What They Are and How to Add Them
Security headers are HTTP response headers that tell browsers how to behave when handling your site's content. They're one of the quickest security wins available — a few lines of Nginx or Apache config can protect against clickjacking, XSS, MIME sniffing, and information leakage. Most sites have none of them configured correctly.
How to check your current security headers
Before adding headers, check what you already have. Use InfiniUm's Security Headers Checker to see all headers with pass/fail/warn status based on OWASP recommendations — or use curl:
curl -I https://yourdomain.com
Look through the response for headers starting with Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, etc.
The six essential security headers
HSTS tells browsers to always use HTTPS for your domain, even if the user types http://. Without it, a user on an insecure network could be downgraded to HTTP before the redirect happens — exposing session cookies and data.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000 means browsers remember the HTTPS-only rule for 1 year. includeSubDomains extends it to all subdomains. preload allows your domain to be included in browser HSTS preload lists — meaning HTTPS is enforced even on the very first visit.
Important: only add preload if you're 100% committed to HTTPS on all subdomains. Preloading is difficult to undo and can take months to propagate.
CSP is the most powerful — and most complex — security header. It tells the browser exactly which sources are allowed to load scripts, styles, images, and other resources. A properly configured CSP prevents cross-site scripting (XSS) attacks from executing injected scripts.
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-RANDOM'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none';
CSP directives:
default-src 'self'— only load resources from your own domain by defaultscript-src— controls JavaScript sources. Avoid'unsafe-inline'and'unsafe-eval'style-src— controls CSS sourcesimg-src— controls image sourcesframe-ancestors 'none'— prevents your page from being embedded in iframes (replaces X-Frame-Options)connect-src— controls fetch, XHR, and WebSocket connections
Start with Content-Security-Policy-Report-Only to test your policy without breaking anything. Use the InfiniUm CSP Generator to build a policy for your specific setup.
Prevents your page from being embedded in an <iframe> on another site — a technique used in clickjacking attacks where an attacker overlays your site invisibly to trick users into clicking something they didn't intend to.
X-Frame-Options: SAMEORIGIN
Options: DENY (no framing at all), SAMEORIGIN (only same-origin frames allowed). Note: if you set frame-ancestors in your CSP, that takes precedence over X-Frame-Options in modern browsers. Set both for compatibility.
Prevents browsers from MIME-sniffing the content type of a response. Without this header, a browser might execute a file labeled as an image as JavaScript if it detects script-like content — a common attack vector.
X-Content-Type-Options: nosniff
This one is simple — always set it to nosniff. There's no reason not to.
Controls how much referrer information is included when a user navigates away from your site. Without this, the full URL (including query parameters with tokens or search terms) can be sent to third-party sites.
Referrer-Policy: strict-origin-when-cross-origin
strict-origin-when-cross-origin sends the full URL for same-origin requests, but only the origin (no path) for cross-origin requests. It's the most widely recommended balance of privacy and functionality.
Controls which browser features your site can use — and crucially, which features third-party scripts embedded on your page can use. Prevents malicious scripts from accessing the camera, microphone, or geolocation without your knowledge.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Empty parentheses () disables the feature entirely. Only enable features your site genuinely needs.
Adding security headers in Nginx
Add these to your server block in /etc/nginx/sites-enabled/yourdomain.conf:
server {
# ... your existing config ...
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none';" always;
server_tokens off;
}
After editing, test and reload:
sudo nginx -t && sudo systemctl reload nginx
Adding security headers in Apache
Add to your .htaccess or VirtualHost configuration. Ensure mod_headers is enabled:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none';"
ServerTokens Prod
Adding security headers in Cloudflare
If you use Cloudflare, you can add headers via Transform Rules (available on Free plan for some headers) or via Workers. For HSTS, Cloudflare has a dedicated toggle under SSL/TLS → Edge Certificates → HTTP Strict Transport Security. CSP and other custom headers are best set at the origin server level.
Common mistakes
- Setting HSTS without HTTPS working correctly — if you add HSTS before your SSL is properly configured, users may be locked out. Always verify HTTPS works first.
- Overly restrictive CSP that breaks functionality — start with
Content-Security-Policy-Report-Onlyand monitor the browser console for violations before enforcing. - Missing
alwaysdirective in Nginx — withoutalways, headers are only added to 200 responses, not redirects or error pages. - Setting headers in the wrong location block — in Nginx, headers in a
locationblock override headers set in theserverblock for that location. Add to theserverblock for site-wide application.