[ GF.dev ] All Tools →

The Complete Guide to HTTP Security Headers

Published 2026-03-29 · Last modified 2026-03-29

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.

What Are HTTP Security Headers?

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.

Why Security Headers Matter

There are several compelling reasons to invest a few minutes in configuring security headers:

The Essential 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.

Strict-Transport-Security (HSTS)

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; preload

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.

Content-Security-Policy (CSP)

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.

X-Frame-Options

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: DENY

Accepted 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.

X-Content-Type-Options

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: nosniff

This 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.

X-XSS-Protection

The X-XSS-Protection header controlled a built-in XSS filter in older browsers. The recommended value was:

X-XSS-Protection: 1; mode=block

However, 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.

Referrer-Policy

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-origin

This 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).

Permissions-Policy

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:

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.

HTTP Public Key Pinning (HPKP) — Deprecated

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.

Implementing Security Headers in Nginx

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.

Implementing Security Headers in Apache

In Apache, you use mod_headers to set response headers. Enable the module first:

sudo a2enmod headers
sudo systemctl restart apache2

Then 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=()"

Testing and Auditing Your Security Headers

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:

  1. Quick scan. Use our security headers audit tool to get an instant grade and a breakdown of every header present or missing on your site.
  2. Command-line check. Use 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.
  3. Browser DevTools. Open the Network tab, select the document request, and inspect the Response Headers section. This confirms what the browser actually receives after any CDN or proxy modifications.
  4. Automated CI checks. Add a header-checking step to your deployment pipeline. A simple script can curl your staging URL and fail the build if critical headers are missing.
  5. CSP report-only mode. Before enforcing a new Content Security Policy, deploy it as 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.

Common Mistakes to Avoid

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-origin

Adjust 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.

Summary

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.