Skip to main content
Back to blog
February 17, 2026|8 min read|Security Basics

What Are HTTP Security Headers and Why They Matter

HTTP security headers are your website's first line of defense. Learn what CSP, HSTS, X-Frame-Options, and other headers do — and how to configure them correctly.

ZeriFlow Team

1,867 words

What Are HTTP Security Headers and Why They Matter

Every time a user visits your website, their browser and your server exchange a series of HTTP headers. Most of these headers handle routine tasks like caching, content types, and encoding. But a special subset of headers exists purely for security, and they are among the most powerful yet underused defenses available to any website owner.

HTTP security headers instruct the browser on how to behave when handling your site''s content. They can prevent cross-site scripting attacks, stop clickjacking, enforce encrypted connections, and block a wide range of client-side exploits. The best part is that they require no changes to your application code. They are pure configuration.

In this guide, we will cover every security header you need to know, explain what each one does, show you how to implement them on popular platforms, and highlight the mistakes that trip up even experienced developers.

Why Security Headers Matter

Modern web attacks increasingly target the browser. Cross-site scripting (XSS), clickjacking, MIME-type confusion, and data injection attacks all exploit the gap between what your server sends and how the browser interprets it.

Security headers close that gap. They tell the browser exactly what is allowed and what is not. Without them, the browser falls back to permissive defaults that attackers can exploit.

Consider this: a single missing header can allow an attacker to embed your login page inside a malicious iframe, trick users into entering credentials, and steal their accounts. That is a real attack vector that the X-Frame-Options header eliminates in one line of configuration.

Beyond defense, security headers also matter for compliance. Frameworks like PCI DSS, SOC 2, and ISO 27001 increasingly expect proper header configuration as part of a secure web deployment. Search engines also factor security signals into ranking, meaning poor header configuration can hurt your SEO.

The Essential Security Headers

Content-Security-Policy (CSP)

Content-Security-Policy is the most powerful and most complex security header. It defines exactly which resources the browser is allowed to load and from which origins.

A properly configured CSP can prevent:

  • Cross-site scripting (XSS) attacks
  • Data injection attacks
  • Clickjacking (via frame-ancestors directive)
  • Mixed content loading
  • Unauthorized script execution

Here is a baseline CSP that you can customize for your site:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

Key directives:

  • default-src 'self' — only allow resources from your own origin by default
  • script-src 'self' — only allow scripts from your origin (blocks inline scripts and external script injection)
  • frame-ancestors 'none' — prevents your site from being embedded in iframes (replaces X-Frame-Options)
  • base-uri 'self' — prevents base tag hijacking
  • form-action 'self' — prevents form submissions to external domains

Common mistake: Starting with an overly permissive CSP like default-src * defeats the purpose entirely. Start strict and add exceptions as needed. Use Content-Security-Policy-Report-Only to test before enforcing.

Strict-Transport-Security (HSTS)

HSTS tells the browser to only connect to your site over HTTPS, even if the user types http:// in the address bar. This prevents SSL stripping attacks where an attacker downgrades the connection to unencrypted HTTP.

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

Parameters:

  • max-age=31536000 — remember this policy for one year (in seconds)
  • includeSubDomains — apply to all subdomains as well
  • preload — request inclusion in browser preload lists so HSTS applies even on the first visit

Important: Only enable HSTS after confirming your entire site works correctly over HTTPS. Once enabled with a long max-age, you cannot easily switch back to HTTP without users being unable to reach your site.

X-Frame-Options

X-Frame-Options prevents your pages from being embedded in iframes on other sites, which is the primary defense against clickjacking attacks.

X-Frame-Options: DENY

Options:

  • DENY — never allow framing (recommended for most sites)
  • SAMEORIGIN — only allow framing from your own origin
  • ALLOW-FROM uri — only allow framing from a specific URI (deprecated, use CSP frame-ancestors instead)

While CSP frame-ancestors is the modern replacement, X-Frame-Options should still be set for compatibility with older browsers.

X-Content-Type-Options

This header prevents the browser from MIME-type sniffing, which is when the browser ignores the declared content type and tries to guess the type based on the content. Attackers can exploit this to execute scripts disguised as other file types.

X-Content-Type-Options: nosniff

There is only one valid value. Always set it.

Referrer-Policy

Referrer-Policy controls how much information about the referring page is sent when navigating away from your site. This prevents leaking sensitive URL parameters or internal paths to third-party sites.

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

Recommended values:

  • strict-origin-when-cross-origin — sends full referrer for same-origin requests, only the origin for cross-origin requests, and nothing when downgrading from HTTPS to HTTP
  • no-referrer — never send referrer information (most private but may break analytics)
  • same-origin — only send referrer for same-origin requests

Permissions-Policy

Permissions-Policy (formerly Feature-Policy) controls which browser features and APIs your site can use. This prevents malicious scripts from accessing the camera, microphone, geolocation, or other sensitive APIs.

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()

Each directive set to () means the feature is disabled entirely. If your site needs a specific feature, you can allow it with camera=(self) to restrict it to your own origin.

X-DNS-Prefetch-Control:

X-DNS-Prefetch-Control: off

Prevents the browser from prefetching DNS for external links, which can leak information about what links are on your page.

Cross-Origin-Opener-Policy (COOP):

Cross-Origin-Opener-Policy: same-origin

Ensures your document does not share a browsing context group with cross-origin documents, protecting against Spectre-type attacks.

Cross-Origin-Resource-Policy (CORP):

Cross-Origin-Resource-Policy: same-origin

Prevents other sites from embedding your resources, adding another layer of protection against data leaks.

How to Implement Security Headers

Nginx

Add these directives to your server block:

nginx
server {
    # Security headers
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
    add_header X-DNS-Prefetch-Control "off" always;
    add_header Cross-Origin-Opener-Policy "same-origin" always;
    add_header Cross-Origin-Resource-Policy "same-origin" always;
}

The always parameter ensures headers are sent even on error responses.

Apache

Add these to your .htaccess or virtual host configuration:

apache
<IfModule mod_headers.c>
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    Header always set X-Frame-Options "DENY"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"
</IfModule>

Vercel (vercel.json)

json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Content-Security-Policy", "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'" },
        { "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=(), payment=()" }
      ]
    }
  ]
}

Cloudflare Workers

javascript
addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const response = await fetch(request);
  const newHeaders = new Headers(response.headers);

  newHeaders.set("Content-Security-Policy", "default-src 'self'; script-src 'self'; frame-ancestors 'none'");
  newHeaders.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
  newHeaders.set("X-Frame-Options", "DENY");
  newHeaders.set("X-Content-Type-Options", "nosniff");
  newHeaders.set("Referrer-Policy", "strict-origin-when-cross-origin");
  newHeaders.set("Permissions-Policy", "camera=(), microphone=(), geolocation=()");

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: newHeaders
  });
}

Common Mistakes to Avoid

1. Setting headers but never testing them. A misconfigured CSP can break your entire site. Always test in report-only mode first, then check for breakage before enforcing.

2. Forgetting error pages. Many servers do not send security headers on 404 or 500 error pages. Use the always directive in Nginx to fix this.

3. Using deprecated header values. X-Frame-Options: ALLOW-FROM is deprecated. Use CSP frame-ancestors instead.

4. Setting conflicting headers. If you set both X-Frame-Options and CSP frame-ancestors, modern browsers will ignore X-Frame-Options in favor of CSP. Make sure they agree.

5. Overly permissive CSP. Directives like script-src 'unsafe-inline' 'unsafe-eval' effectively disable CSP protection for scripts. Use nonces or hashes instead.

6. Missing the Strict-Transport-Security header on subdomains. If you use includeSubDomains, make sure all subdomains actually support HTTPS first.

7. Not removing the Server header. While not a security header per se, the Server header leaks information about your web server software and version. Remove or obscure it.

How to Test Your Security Headers

Manual testing is slow and error-prone. Use automated scanning to check your headers regularly.

ZeriFlow scans your website and checks all security headers automatically, giving you a clear score and actionable recommendations. Run a free scan now to see exactly which headers you are missing and how to fix them.

For ongoing monitoring, the ZeriFlow advanced scan provides deep analysis of your header configuration, including CSP policy evaluation, HSTS preload readiness, and compatibility checks across browsers.

Summary

Security headers are one of the highest-impact, lowest-effort security improvements you can make. They require no code changes, protect against real attack vectors, and can be implemented in minutes.

Here is your action plan:

  1. 1Scan your site with ZeriFlow to see your current header status
  2. 2Start with the easy wins: X-Content-Type-Options, X-Frame-Options, Referrer-Policy
  3. 3Add HSTS once you have confirmed full HTTPS support
  4. 4Build a CSP in report-only mode, test, then enforce
  5. 5Add Permissions-Policy to disable unused browser APIs
  6. 6Re-scan to confirm your improvements

Do not leave your users unprotected. Security headers take minutes to implement and provide lasting defense against some of the most common web attacks.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading