Why Security Headers Matter
HTTP security headers are instructions your Nginx server sends back with every response, telling the browser how to behave defensively when it renders your site. They don't change your application code at all; they simply add a layer of policy enforcement at the edge, closing off entire classes of attacks like clickjacking, MIME-type confusion, and script injection before they can succeed.
Cricket analogy: Think of security headers like the third umpire's protocols in cricket - the on-field umpire (your app) makes the call, but the third umpire (the browser) is instructed to double-check specific things like no-balls and boundary lines before the result stands.
Core Headers to Configure
The Strict-Transport-Security (HSTS) header tells the browser to only ever connect to your domain over HTTPS for a specified duration, preventing SSL-stripping attacks where a man-in-the-middle downgrades the connection to plain HTTP. X-Content-Type-Options: nosniff stops the browser from guessing a file's MIME type based on its content, which closes off attacks where a malicious file disguised as an image is executed as a script.
Cricket analogy: HSTS is like a batsman who, once he's decided to play only on true, well-covered pitches, refuses to walk out onto a substandard wicket even if the ground staff insists it's fine - the decision is locked in ahead of time.
# /etc/nginx/conf.d/security-headers.conf
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self';" always;The always flag on add_header ensures the header is sent even on error responses (4xx/5xx), which is easy to forget and leaves error pages unprotected.
Content-Security-Policy in Depth
Content-Security-Policy (CSP) is the most powerful but also the most fragile header, because it whitelists exactly which sources scripts, styles, images, and frames may load from. A misconfigured default-src 'self' will silently break any inline script or third-party widget your site relies on, so CSP rollout should start in Content-Security-Policy-Report-Only mode to observe violations before enforcing them.
Cricket analogy: It's like a captain setting an extremely tight fielding ring for a death-over specialist - highly effective at stopping boundaries, but if misjudged it also chokes off the singles your own bowler needs to rotate strike.
Never deploy Content-Security-Policy directly to production without first testing in Content-Security-Policy-Report-Only mode - an overly strict policy can silently break login forms, payment widgets, or analytics scripts.
- add_header directives in Nginx inject response headers that instruct the browser to defend itself, without requiring any application code changes.
- Always use the
alwaysflag so headers are also sent on error responses (4xx/5xx), not just 2xx. - Strict-Transport-Security (HSTS) forces HTTPS-only connections for a specified max-age, preventing SSL-stripping downgrade attacks.
- X-Content-Type-Options: nosniff stops browsers from MIME-sniffing content into an unintended, potentially executable type.
- X-Frame-Options and frame-ancestors in CSP prevent clickjacking by controlling whether your site can be embedded in an iframe.
- Content-Security-Policy is the most powerful header but also the easiest to misconfigure; roll it out in Report-Only mode first.
- Headers set in a server block are not automatically inherited the same way in nested location blocks once any add_header appears in that location - all headers must be repeated.
Practice what you learned
1. Which Nginx directive flag ensures a security header is also sent on 4xx/5xx error responses?
2. What is the primary purpose of the Strict-Transport-Security header?
3. Why should Content-Security-Policy be tested with Content-Security-Policy-Report-Only before enforcing it?
4. What does X-Content-Type-Options: nosniff prevent?
5. If a location block already has one add_header directive, what happens to headers defined in the parent server block for that location?
Was this page helpful?
You May Also Like
Nginx Hardening Checklist
A practical, prioritized checklist for locking down a production Nginx deployment, from TLS configuration to information disclosure and module minimization.
Nginx and Let's Encrypt
Automate free, trusted TLS certificates for Nginx using Let's Encrypt and Certbot, including auto-renewal and HTTPS redirection.
Restricting Access with Nginx
Control who can reach specific routes and resources using Nginx's IP allow/deny rules, HTTP basic auth, and satisfy directives.
Related Reading
Related Study Notes in DevOps
Browse all study notesAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics