by Host Autopsy Editorial

HTTP Security Headers: The Complete Checklist

Protect your website with the right HTTP security headers. HSTS, CSP, X-Frame-Options, and more — what they do and how to set them up.

Why Security Headers Matter

Security headers are instructions your server sends to the browser about how to handle your content. They prevent clickjacking, XSS attacks, data injection, and protocol downgrades — with zero impact on user experience. Your visitors never see them, but attackers do.

Most websites are missing critical security headers. A 2025 study found that only 20% of the top million websites had a Content-Security-Policy, and 35% were missing basic protections like X-Content-Type-Options. These are free, easy fixes.

The Essential Headers

1. Strict-Transport-Security (HSTS)

What it prevents: SSL stripping attacks, where an attacker downgrades your HTTPS connection to HTTP to intercept traffic.

Forces browsers to always use HTTPS for your domain. Once a browser sees this header, it will refuse to connect over HTTP — even if the user types http:// or clicks an HTTP link.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • max-age=31536000 — Remember for 1 year (31.5 million seconds)
  • includeSubDomains — Apply to all subdomains (api.example.com, cdn.example.com, etc.)
  • preload — Submit to browser preload lists for ultimate protection (browsers will enforce HTTPS before ever connecting)

Warning: Only add preload after you're certain HTTPS works perfectly on all subdomains. Getting into the preload list is easy; getting removed takes months.

2. Content-Security-Policy (CSP)

What it prevents: Cross-site scripting (XSS), data injection, and unauthorized resource loading.

Controls exactly which resources the browser can load on your pages — scripts, styles, images, fonts, frames, everything. This is the most powerful security header and the most complex to configure.

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'none'

Start with Report-Only mode to test without breaking anything:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

This logs violations without blocking them, so you can see what would break before enforcing.

CSP breakdown by directive:

| Directive | Controls | Example | |-----------|----------|---------| | default-src | Fallback for all resource types | 'self' | | script-src | JavaScript sources | 'self' 'unsafe-inline' https://cdn.example.com | | style-src | CSS sources | 'self' 'unsafe-inline' | | img-src | Image sources | 'self' data: https: | | font-src | Font file sources | 'self' https://fonts.gstatic.com | | connect-src | XHR, fetch, WebSocket targets | 'self' https://api.example.com | | frame-ancestors | Who can embed your site in iframes | 'none' or 'self' | | upgrade-insecure-requests | Auto-upgrade HTTP to HTTPS | (no value needed) |

3. X-Content-Type-Options

What it prevents: MIME type confusion attacks, where a browser interprets a file as a different type than declared (e.g., treating a text file as executable JavaScript).

X-Content-Type-Options: nosniff

This header has one value. Always set it. There's no reason not to.

4. X-Frame-Options

What it prevents: Clickjacking — an attacker embeds your site in an invisible iframe and tricks users into clicking buttons they can't see.

X-Frame-Options: DENY

Options:

  • DENY — No one can embed your site in an iframe (safest)
  • SAMEORIGIN — Only your own domain can embed it (use if you iframe your own content)

Note: The frame-ancestors directive in CSP is the modern replacement, but X-Frame-Options is still needed for older browser support. Set both.

5. Referrer-Policy

What it controls: How much information is sent in the Referer header when users click links to other sites.

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

This sends the full URL for same-site navigation but only the origin (domain) for cross-site links. This prevents leaking sensitive URL paths (like /account/settings/security) to third-party sites.

| Policy | Same-site | Cross-site | Use Case | |--------|-----------|------------|----------| | no-referrer | Nothing | Nothing | Maximum privacy | | strict-origin-when-cross-origin | Full URL | Origin only | Recommended default | | origin | Origin only | Origin only | Good balance | | unsafe-url | Full URL | Full URL | Avoid — leaks paths |

6. Permissions-Policy

What it controls: Which browser features your site can use — camera, microphone, geolocation, payment API, etc.

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

Empty parentheses () means the feature is disabled entirely. If your site doesn't use the camera, disable it — this prevents any injected script from accessing it.

Specify allowed origins if you need a feature:

Permissions-Policy: geolocation=(self "https://maps.example.com")

How to Set Security Headers

Nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

The always keyword ensures headers are sent on error pages too (40x, 50x responses), not just successful ones.

Apache (.htaccess)

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
      ]
    }
  ]
}

Next.js (next.config.js)

const securityHeaders = [
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
];

module.exports = {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }];
  },
};

Cloudflare

Use Transform Rules (Rules → Transform Rules → Modify Response Header) to add headers to all responses without touching your origin server. This is the easiest approach if you're already using Cloudflare.

Security Header Grades

SecurityHeaders.com grades your site A through F based on which headers are present:

| Grade | What You Have | What You're Missing | |-------|--------------|-------------------| | A+ | All 6 essential headers + CSP | Nothing | | A | 5 headers, basic CSP | Strict CSP or Permissions-Policy | | B | 3-4 headers | CSP and 1-2 others | | C | 1-2 headers | Most protections | | D/F | None or almost none | Critical protections |

Implementation Priority

If you can only add headers in stages, this is the order:

  1. X-Content-Type-Options: nosniff — One line, zero risk, protects against MIME confusion
  2. X-Frame-Options: DENY — One line, zero risk (unless you iframe your own site)
  3. Strict-Transport-Security — Requires working HTTPS first, then one line
  4. Referrer-Policy — One line, minor privacy improvement
  5. Permissions-Policy — Disable features you don't use
  6. Content-Security-Policy — Most impactful but requires testing — use Report-Only first

Test Your Headers

Run your site through our scanner above to check which security headers you're missing and get a security score. The scanner checks all six essential headers plus your SSL configuration, DNS health, and page speed.

Frequently Asked Questions

Which security headers are most important?
Start with these three: Strict-Transport-Security (HSTS) to force HTTPS, X-Content-Type-Options to prevent MIME sniffing, and X-Frame-Options to prevent clickjacking. These three cover the most common attack vectors and take seconds to add.
Will security headers break my website?
Most headers are safe to add immediately — HSTS, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy won't break anything. Content-Security-Policy (CSP) is the exception — a misconfigured CSP can block legitimate resources. Always test CSP with Content-Security-Policy-Report-Only first.
How do I check what security headers my site has?
Use our scanner tool, SecurityHeaders.com, or open Chrome DevTools → Network tab → click any request → look at the Response Headers section. Missing headers are the ones you need to add.
Do I need security headers on a static website?
Yes. Even static sites can be targeted by clickjacking (embedding your site in a malicious iframe), protocol downgrade attacks (stripping HTTPS), and content type confusion attacks. The headers take minutes to set up regardless of site type.

Related Articles

Check your own website

Run a free scan to check SSL, DNS, speed, and security headers.

Scan Your Site Free →