Home › Blog › Hardening · Guide
Security Headers Every Website Should Send (10-Minute Setup)
Websites need a handful of response headers to tell browsers how to handle content and connections.
Strict-Transport-Security (HSTS)
HSTS instructs browsers to always use HTTPS when connecting to the site, preventing accidental fallback to insecure HTTP. The directive also includes subdomains, so every part of the domain inherits the protection once the header is in place. It should only be enabled after HTTPS works everywhere, otherwise users could be locked out of parts of the site.
When a browser receives the HSTS header, it stores the rule locally and refuses any future HTTP request for the domain. This means that even if an attacker tries to serve a page over plain HTTP, the browser will automatically upgrade the request to HTTPS before the connection is made.
X-Content-Type-Options: nosniff
The nosniff directive tells browsers not to guess a file’s MIME type based on its content. By forcing the browser to honor the declared Content‑Type, the site eliminates a class of content‑type confusion bugs that attackers often exploit to inject malicious code.
This behavior helps developers notice misconfigured assets early and prevents browsers from inadvertently executing files that were meant to be served as static resources.
Content‑Security‑Policy (CSP) – Report‑Only Mode
CSP defines where scripts, styles, images, and other resources may be loaded from, dramatically reducing the risk of injection attacks. Starting with a Report‑Only policy lets the site observe violations without breaking functionality, making it safe to roll out gradually. A minimal policy that allows only resources from the same origin (default‑src 'self') blocks external scripts and frames that were not explicitly permitted.
When a violation occurs, the browser sends a report to the endpoint specified in the policy, allowing developers to see what would have been blocked. This feedback loop helps fine‑tune the policy before enforcing it fully. By restricting the sources of executable content, the site prevents attackers from loading malicious code from third‑party domains, even if they manage to inject a reference into the page.
Referrer‑Policy: strict-origin-when-cross-origin
The Referrer‑Policy controls what information is sent in the Referer header when a user follows a link. The strict‑origin‑when‑cross‑origin setting sends the full URL only to same‑origin destinations, while cross‑origin requests include only the scheme, host and port. This limits the amount of potentially sensitive path data that leaves the site, protecting user privacy and reducing the exposure of internal routing structures.
By adopting this policy, the site avoids leaking query strings, authentication tokens, or other identifiers to external services. Browsers automatically apply the rule to all outbound navigation, so developers do not need to add custom logic. The result is a consistent privacy posture that aligns with modern best practices for data minimization.
Permissions‑Policy
Permissions‑Policy replaces the older Feature‑Policy header and lets a site explicitly disable browser features it does not use, such as geolocation, microphone or camera. By setting each feature to an empty list, the browser knows that the site never needs to request those capabilities, preventing malicious scripts from prompting users for access they never intended to grant.
When the policy is in place, attempts by any script to invoke a disabled feature are silently ignored, and the user never sees a permission prompt. This reduces the attack surface for social‑engineering tricks that rely on convincing a user to share sensitive hardware resources.
Clickjacking Protection
Clickjacking occurs when an attacker embeds a site inside an invisible frame and tricks users into clicking hidden elements. The X‑Frame‑Options header with the SAMEORIGIN value tells browsers to allow framing only by pages on the same domain, effectively blocking external sites from embedding the content. An alternative CSP directive, frame‑ancestors 'self', achieves the same result with more flexibility for future extensions.
When a browser receives either of these directives, it refuses to render the page inside a frame that does not match the allowed source. This stops malicious pages from overlaying the site with deceptive UI elements, protecting users from unintended actions such as accidental purchases or credential submissions. The protection is enforced entirely by the browser, requiring no additional client‑side code.
Adding the Headers in nginx and Verifying
In nginx, the headers can be added with the add_header directive inside the server block. The X‑Content‑Type‑Options and Referrer‑Policy headers are set to their recommended values and marked as always, ensuring they appear even on error responses. The other headers follow the same pattern, with each directive specifying the exact value described in the technical notes.
After reloading the nginx configuration, verification is straightforward. Running a curl request with the -I flag against the domain prints the response headers, allowing a quick visual check that each header is present. For a more comprehensive view, free online tools such as the Mozilla Observatory or securityheaders.com scan the site and report any missing or misconfigured headers, giving clear guidance on what still needs attention.
Copy-paste reference
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Common questions
Can I enable HSTS on a site that still serves some HTTP pages?
HSTS should be turned on only after every page on the domain works over HTTPS, otherwise browsers will refuse to load the remaining HTTP content.
What happens if a CSP violation is reported?
In Report‑Only mode the browser sends a JSON report to the endpoint you specify, allowing you to see which resources would have been blocked without disrupting users.