HTTP Security Headers Every Website Should Implement, and Why Each One Matters

A handful of response headers, each a single line, close off entire categories of browser-side attack for close to zero implementation cost on most platforms. Most small sites still ship none of them.

Each header is a single, low-cost instruction telling the browser to enforce a specific protection on your behalf.

In this post

  1. Strict-Transport-Security
  2. Content-Security-Policy, briefly
  3. X-Content-Type-Options
  4. X-Frame-Options and frame-ancestors
  5. Referrer-Policy
  6. Permissions-Policy
  7. How to check what you already have, and add what you do not

HTTP security headers are instructions your server sends alongside every response, telling the visitor's browser to enforce specific protections automatically, without requiring any change to your actual application code in most cases. They are among the highest-value, lowest-effort security improvements available to a site owner, and this post covers the specific headers worth prioritizing, and precisely what attack each one addresses.

HeaderWhat it protects against
Strict-Transport-SecurityDowngrade and interception attacks on the initial HTTP request
Content-Security-PolicyCross-site scripting and unauthorized resource loading
X-Content-Type-OptionsMIME-type sniffing attacks
X-Frame-Options / frame-ancestorsClickjacking
Referrer-PolicyLeaking sensitive URL data to third-party sites
Permissions-PolicyUnauthorized use of browser features like camera or location

Strict-Transport-Security

This header, commonly called HSTS, tells the browser to only ever connect to your site over HTTPS, for a specified duration, even if a visitor types your domain without the protocol or clicks an old HTTP link. Without it, that very first request can go out over plain HTTP before any redirect to HTTPS happens, a brief window during which an attacker on the same network, a public Wi-Fi network, for instance, could intercept or modify that initial request before the redirect ever occurs. A typical implementation looks like this:

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

The includeSubDomains directive extends this protection to every subdomain, and sites confident in their full HTTPS coverage can additionally submit to browser preload lists, which hardcode the HSTS policy into the browser itself before the site is ever visited for the first time at all.

Content-Security-Policy, briefly

Covered in full depth in an earlier post on this blog, CSP restricts which sources of scripts, styles, and other content a page is allowed to load, providing a real backstop against cross-site scripting even when input sanitization elsewhere in an application has a gap. Given its complexity and the real risk of breaking site functionality if deployed carelessly, it is worth implementing through the report-only rollout process described in that dedicated post rather than treated as a quick addition here.

X-Content-Type-Options

This header, with the single valid value nosniff, prevents the browser from trying to guess, or "sniff," a resource's content type differently from what the server's Content-Type header actually declares. Without it, a browser might interpret a file uploaded and served as an image, for instance, as executable script instead, based on its own guess about the file's actual content, opening a path for an attacker to disguise malicious script content as an innocuous file type and have the browser execute it anyway. This header closes that gap directly:

X-Content-Type-Options: nosniff

X-Frame-Options and frame-ancestors

These headers control whether your site can be loaded inside an iframe on another site, directly addressing clickjacking, an attack where a malicious site overlays an invisible iframe of your legitimate site on top of its own content, tricking a visitor into clicking something on your site, a purchase confirmation or a permission grant, while believing they are interacting with the attacker's page instead. The modern approach uses CSP's frame-ancestors directive, with the older X-Frame-Options header retained for broader legacy browser compatibility:

X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

Use SAMEORIGIN instead of DENY if your own site legitimately needs to frame its own pages, which is common for certain embedded widget or preview features.

Referrer-Policy

By default, when a visitor clicks a link from your site to another, their browser can send the full URL of the page they came from, including any sensitive data that happened to be in the URL itself, a search query or a session identifier in rare, poorly designed implementations, to the destination site as part of the referrer header. This header controls how much of that referring URL gets shared:

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

This particular value, a widely recommended default, sends the full URL for same-site navigation while limiting cross-site navigation to just the origin, a reasonable balance between analytics usefulness and avoiding unnecessary data leakage to external sites.

Permissions-Policy

This header lets a site explicitly declare which powerful browser features, camera, microphone, geolocation, and others, it and any embedded third-party content are allowed to use, providing a real restriction even against a compromised or malicious third-party script or iframe attempting to request access to hardware your site never intended to use in the first place:

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

This example explicitly disables all three listed features entirely; adjust the list to match what your specific site legitimately needs, allowing only those specific features rather than leaving broad, unstated defaults in place.

Being honest about the effort-to-value ratio here: unlike CSP, which genuinely requires careful, staged rollout to avoid breaking site functionality, most of the headers above are close to zero-risk to add on most standard sites, they do not typically require application code changes, and the failure mode of getting one slightly wrong is generally a browser console warning rather than a broken site. This combination of low effort and real protective value is unusually favorable compared to most security recommendations, which is exactly why it is worth prioritizing even for a small site with limited time to spend on security work.

How to check what you already have, and add what you do not

The SecurityHeaders.com scanner checks any public URL against this exact set of headers in seconds, providing a clear, specific report of what is present, what is missing, and a letter grade summarizing the overall posture, a genuinely useful starting point before making any changes. Implementation specifics vary by platform, most major web servers, Nginx and Apache included, and most CMS platforms through either configuration or a dedicated security plugin, support adding these headers without custom application code, and Mozilla's HTTP headers reference on MDN covers the full technical specification for each one in more depth than this post's practical summary.

None of these headers alone makes a site fully secure, and none of them replaces the underlying application security practices covered elsewhere on this blog. What they do, reliably and at very low cost, is close off several entire categories of browser-side attack that would otherwise remain open by default, which makes them one of the better returns on a small amount of implementation time available to any site owner.