HTTP Security Headers Every Sysadmin Should Know

When a browser receives a response from a web server, the visible content is only part of what arrives. HTTP response headers travel alongside every page, image, and API response — and several of them exist purely to tell the browser how to behave securely. Setting them correctly is one of the lowest-effort, highest-impact things you can do for a site's security posture.

Most of these headers require a single line of configuration. Most sites are missing at least half of them.

What HTTP security headers are

HTTP headers are key-value pairs sent by the server before the response body. Security headers are a subset that instruct the browser to enforce specific behaviours — blocking certain resource loads, refusing to render inside an iframe, or upgrading connections to HTTPS. They don't replace application-level security, but they close off whole categories of attack that would otherwise rely on browser defaults being permissive.

The seven headers that matter

Strict-Transport-Security (HSTS)

HSTS tells the browser to always use HTTPS for this domain, even if the user types http:// or follows a plain HTTP link. Once a browser has seen the header, it enforces HTTPS internally without making an initial HTTP request — eliminating SSL-stripping attacks.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

max-age is in seconds (31536000 = one year). includeSubDomains extends the policy to all subdomains. preload opts the domain into browser preload lists, so HTTPS is enforced even on the first visit.

Content-Security-Policy (CSP)

CSP is the most powerful security header and the most complex to configure correctly. It tells the browser which origins are allowed to supply scripts, styles, images, fonts, and other resources. A properly configured CSP prevents cross-site scripting (XSS) by blocking inline scripts and untrusted external sources.

Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com

Start with a report-only mode (Content-Security-Policy-Report-Only) to identify what would be blocked before enforcing it.

X-Frame-Options

This header prevents the page from being embedded inside an <iframe> on another domain. Without it, attackers can layer an invisible iframe over a legitimate page and trick users into clicking on elements they can't see — a clickjacking attack.

X-Frame-Options: DENY

DENY blocks all framing. SAMEORIGIN allows framing only from the same origin. Note that CSP's frame-ancestors directive supersedes this header in modern browsers, but X-Frame-Options provides coverage for older clients.

X-Content-Type-Options

This one-value header tells browsers not to MIME-sniff responses — that is, not to guess the content type and override the Content-Type header. MIME sniffing can turn a seemingly harmless file upload into an executable script.

X-Content-Type-Options: nosniff

Referrer-Policy

When a user clicks a link, the browser typically sends a Referer header to the destination server containing the full URL of the originating page. This can leak sensitive path information (session IDs in URLs, internal tool names, etc.). The Referrer-Policy header controls how much of this URL is shared.

Referrer-Policy: strict-origin-when-cross-origin

This value sends the full URL for same-origin requests but only the origin (not the full path) for cross-origin requests, and nothing at all when downgrading from HTTPS to HTTP.

Permissions-Policy

Formerly named Feature-Policy, this header restricts which browser APIs the page and its embedded iframes can access — camera, microphone, geolocation, payment, and more. Even if a third-party script is loaded, it cannot access a feature you've disabled here.

Permissions-Policy: camera=(), microphone=(), geolocation=()

Empty parentheses mean the feature is disabled for all origins including the page itself.

X-XSS-Protection

This is a legacy header that activated a built-in XSS filter in older versions of Internet Explorer and Chrome. Modern browsers have removed this filter entirely, so the header has no effect on current clients. It's still worth setting to avoid security scanners flagging its absence, but don't rely on it for actual protection — that's CSP's job.

X-XSS-Protection: 1; mode=block

How to add security headers

The right approach depends on your stack. In nginx, add headers inside a server or location block:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;

In Apache, use mod_headers in your .htaccess or VirtualHost block:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"

If you're using Cloudflare, the easiest route is Transform Rules (under Rules in the dashboard). Create a response header modification rule and add the headers you need — no server config changes required. This is particularly useful if you don't control the origin server directly.

After making changes, run the analyzer below to confirm the headers are being sent correctly.

Security Headers Analyzer — Enter any domain to instantly check which of the seven key security headers are present, see their current values, and get a summary score.

Open Tool