How to Add Security Headers to Nginx (CSP, X-Frame-Options, HSTS)
In this tutorial, you'll learn about How to Add Security Headers to Nginx (CSP, X. We cover key concepts, practical examples, and best practices.
The Problem
Your Nginx web application has low security headers score on security testing tools (e.g., Mozilla Observatory) because it is missing Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, and other important response headers.
Quick Fix
Add Basic Security Headers
sudo tee /etc/nginx/conf.d/security-headers.conf << 'EOF'
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;
EOF
sudo nginx -t && sudo systemctl reload nginx
# nginx: the configuration file syntax is ok
X-Frame-Options: SAMEORIGIN prevents clickjacking. X-Content-Type-Options: nosniff prevents MIME type sniffing. Referrer-Policy controls referrer information sent with requests.
Add Content-Security-Policy
sudo tee -a /etc/nginx/conf.d/security-headers.conf << 'EOF'
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self';" always;
EOF
sudo nginx -t && sudo systemctl reload nginx
# nginx: the configuration file syntax is ok
CSP restricts which sources can load content on your page. Start with a restrictive policy and relax it as needed. Use report-uri or report-to for violation monitoring.
Enable HSTS (HTTP Strict Transport Security)
sudo tee -a /etc/nginx/conf.d/security-headers.conf << 'EOF'
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
EOF
sudo nginx -t && sudo systemctl reload nginx
# nginx: the configuration file syntax is ok
HSTS tells browsers to always use HTTPS. Set max-age to 2 years (63072000s) and add includeSubDomains. Submit to the HSTS preload list after confirming all subdomains support HTTPS.
Verify Headers Are Being Sent
curl -I https://yourdomain.com
# HTTP/2 200
# server: nginx
# x-frame-options: SAMEORIGIN
# x-content-type-options: nosniff
# strict-transport-security: max-age=63072000; includeSubDomains
# content-security-policy: default-src 'self'; ...
Use curl -I to inspect the response headers. All configured security headers should appear. If a header is missing, check for conflicting add_header directives (inner blocks override outer blocks).
Use Permissions-Policy to Restrict Browser Features
sudo tee -a /etc/nginx/conf.d/security-headers.conf << 'EOF'
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
EOF
The Permissions-Policy header restricts which browser features your site can use. This adds an extra layer of security by disabling features like the camera, microphone, and geolocation by default.
Additional Troubleshooting
# Check the error message and stack trace for more context
echo "Review the full error output to identify the root cause"
If the above steps do not resolve the issue, examine the complete error message and stack trace. Often the key detail is in the middle of the traceback rather than the final line. Search for the error message in the project documentation or issue tracker for additional solutions.
Prevention
- Always add the
alwaysparameter to security headers so they are sent on all status codes (including errors) - Test your site on Mozilla Observatory or securityheaders.com after making changes
- Use
add_headerin theserverblock for site-wide headers and in individuallocationblocks for overrides - Start with a strict CSP and use
report-urito monitor violations before enforcing it
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro