HTTP security headers are one of the most effective and least expensive ways to harden your web application against common attacks. By adding a handful of response headers, you can instruct browsers to enforce HTTPS, block clickjacking, prevent cross-site scripting, and stop a range of injection-based vulnerabilities — all without changing a single line of application code.
Despite their power, security headers remain widely misconfigured. A 2025 scan of the top one million websites found that fewer than 25% deployed a meaningful Content Security Policy, and nearly 40% still lacked basic protections like X-Content-Type-Options. The gap between what is available and what is deployed represents a massive, avoidable attack surface.
This guide covers every security header you need to know in 2026, explains what each one does, shows exactly how to configure them in Nginx and Apache, and links to free tools so you can audit your security headers right now. Whether you are a developer shipping a side project or a sysadmin hardening production infrastructure, this is the single reference you need.
Every time a browser requests a page, the server responds with HTTP headers before the body content arrives. Most headers are mundane — Content-Type, Cache-Control, Date — but a special subset exists purely to tell the browser how to behave from a security standpoint. These are collectively called HTTP security headers.
Security headers work on the principle of defense in depth. They do not replace secure coding practices, input validation, or proper authentication. Instead, they add a browser-enforced safety net that limits the damage when something inevitably goes wrong. A cross-site scripting flaw is far less exploitable when a strict Content Security Policy blocks inline script execution. A compromised ad network cannot frame your login page when X-Frame-Options is set to DENY.
Because these headers are enforced by the browser, they protect every user who visits your site with a modern browser — no client-side library or browser extension required.
There are several compelling reasons to invest a few minutes in configuring security headers:
Below is the complete list of security headers you should evaluate for every production site. We cover each in detail, and many have their own dedicated deep-dive article.
HSTS tells the browser to always connect to your site over HTTPS, even if the user types http:// in the address bar. This eliminates the brief, dangerous window of an unencrypted first request that attackers can exploit with SSL-stripping tools like sslstrip.
A recommended HSTS header looks like this:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadmax-age — how long (in seconds) the browser should remember to force HTTPS. Two years (63072000) is the standard for production.includeSubDomains — applies the policy to every subdomain, which is critical because a single insecure subdomain can set cookies for the apex domain.preload — signals your intent to be included in the browser HSTS preload list, which ships the policy with the browser itself, eliminating even the very first insecure request.To learn more about configuring HSTS correctly, including common pitfalls and preload submission, read our detailed guide: What is HSTS and How to Configure It Properly. You can also test your HSTS configuration instantly.
CSP is the single most powerful security header available. It lets you define an allowlist of sources from which the browser may load scripts, styles, images, fonts, frames, and other resources. Anything not on the list is blocked.
A minimal CSP that blocks inline scripts and limits sources to your own origin:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'CSP is also your most effective defense against cross-site scripting (XSS). When script-src does not include 'unsafe-inline', injected script tags simply will not execute. For a complete walkthrough, see Content Security Policy (CSP) Explained: From Basics to Advanced. You can test your CSP configuration to find weaknesses.
This header prevents your pages from being embedded in <iframe>, <frame>, or <object> elements on other sites, which is the core defense against clickjacking attacks.
X-Frame-Options: DENYAccepted values are DENY (never allow framing), SAMEORIGIN (allow framing only by the same origin), or ALLOW-FROM uri (allow framing only by the specified origin — note that this directive is deprecated and not supported in modern browsers).
The modern replacement is Content-Security-Policy: frame-ancestors 'none', which offers more granular control. Best practice is to set both headers for backward compatibility. Read more in Preventing Clickjacking with X-Frame-Options and CSP frame-ancestors. You can test your X-Frame-Options configuration to confirm protection.
Browsers historically tried to be helpful by guessing ("sniffing") the MIME type of a response when the server-provided type seemed wrong. Attackers exploit this behavior: upload a file named avatar.jpg that actually contains JavaScript, and if the browser sniffs it as text/html, the script executes.
X-Content-Type-Options: nosniffThis single directive disables MIME sniffing entirely. There is no reason not to set it on every site. For the full explanation, see X-Content-Type-Options: Why MIME Sniffing is Dangerous.
The X-XSS-Protection header controlled a built-in XSS filter in older browsers. The recommended value was:
X-XSS-Protection: 1; mode=blockHowever, this header is now considered deprecated. Chrome removed its XSS Auditor in version 78 (2019), Edge followed suit, and Firefox never implemented it. The auditor itself was found to introduce new vulnerabilities in some cases.
In 2026, the correct approach is to rely on a strong Content Security Policy instead. Some security auditors still flag the missing header, so many teams set X-XSS-Protection: 0 to explicitly disable the broken legacy filter. Read the full analysis in XSS Protection Headers: What Still Works in 2026.
When a user clicks a link from your site to another, the browser sends a Referer header containing the originating URL. This can leak sensitive path information, query parameters (sometimes containing tokens), and internal URL structure.
Referrer-Policy: strict-origin-when-cross-originThis policy sends only the origin (e.g., https://example.com) on cross-origin requests, but sends the full URL for same-origin navigation, which is useful for analytics. Other useful values include no-referrer (send nothing) and same-origin (send the full referrer only for same-origin requests).
Formerly known as Feature-Policy, this header controls which browser features (camera, microphone, geolocation, payment, etc.) your site and any embedded iframes may use.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self)The empty parentheses () completely disable the feature. This is important even if your application does not use the camera, because a compromised third-party script embedded on your page could attempt to access it.
While not technically an HTTP response header in the same class, the Set-Cookie header carries critical security attributes that belong in any security headers discussion:
Secure — cookie is only sent over HTTPS.HttpOnly — cookie cannot be read by JavaScript, neutralizing most XSS-based session theft.SameSite=Lax or SameSite=Strict — prevents the cookie from being sent on cross-site requests, which is the primary defense against CSRF attacks.Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/For a thorough treatment of each attribute and its interaction with modern browser defaults, see Cookie Security: Secure, HttpOnly, SameSite Explained. You can test your cookie security to verify your configuration.
HPKP allowed site operators to pin specific TLS certificate public keys, preventing mis-issued certificates from being accepted. It sounded good in theory, but in practice it was extremely dangerous: a misconfiguration or lost key could permanently lock users out of your site for the entire max-age duration.
Chrome removed HPKP support in version 72, and no major browser supports it today. The successor technology is Certificate Transparency (CT), which achieves similar goals through public certificate logs that can be monitored without the risk of self-inflicted denial of service. Do not deploy HPKP.
Nginx makes it straightforward to add security headers globally. Place these directives in your server block or in a shared include file:
# /etc/nginx/snippets/security-headers.conf
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
Then include it in each server block:
server {
listen 443 ssl http2;
server_name example.com;
include snippets/security-headers.conf;
# ... rest of config
}Important Nginx caveat: if you use add_header inside a location block, it overrides all add_header directives from the parent server block. Use the always parameter and consider the ngx_headers_more module for more reliable behavior.
In Apache, you use mod_headers to set response headers. Enable the module first:
sudo a2enmod headers
sudo systemctl restart apache2Then add the headers in your virtual host or .htaccess:
# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "0"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Deploying headers is only half the job. You need to verify they are actually being sent, that the values are correct, and that you have not introduced regressions. Here is a practical workflow:
curl -I https://example.com to inspect the raw response headers. Pipe to grep -i 'strict\|content-security\|x-frame\|x-content\|referrer\|permissions' for a focused view.curl your staging URL and fail the build if critical headers are missing.Content-Security-Policy-Report-Only to collect violation reports without breaking anything. This lets you iteratively tighten the policy.For a step-by-step walkthrough, see How to Audit Your Security Headers in 5 Minutes.
always directive in Nginx and Header always set in Apache.'unsafe-inline' 'unsafe-eval' in script-src provides almost no XSS protection. Start strict and loosen only where necessary, using nonces or hashes instead of unsafe directives.includeSubDomains, those subdomains will become unreachable. Audit all subdomains first.Strict-Transport-Security but serving session cookies without Secure is contradictory. Treat cookie security attributes as part of your header strategy.If you want a quick-start set of headers that provides strong protection for most websites, here is the baseline we recommend:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self)
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-originAdjust the CSP as needed for your specific application. The other headers can usually be deployed exactly as shown. After deploying, audit your security headers to confirm everything is in place.
HTTP security headers are not optional — they are a fundamental layer of web application security. The time investment is minimal (often under 30 minutes for a complete deployment), the risk is low, and the protection is immediate and broad. Start with the recommended baseline above, test with our free tools, and then read the deep-dive articles linked throughout this guide to fine-tune each header for your environment.