Free · no account required

Security headers checker

Check the HTTP security headers of a public website online. Enter a domain to see how eight key security headers are rated on the final response and, where shown, the value observed.

A free check of public website settings. Your website is not changed.

No signup No destructive traffic Public website checks
Header reference

What each header does and how it is rated

The checker reads these headers on one response. Most rows are presence checks: a header that is sent counts, and its value is shown where available so you can judge whether it suits your site.

HeaderWhat it doesWhat good looks likeHow this checker rates it
Strict-Transport-SecurityMakes returning browsers switch to HTTPS on their own, before any request leaves in plain HTTP.A long max-age (180 days or more) once your ramp-up is done; the guide covers the stages.Missing on an HTTPS target: medium. Not rated when you enter an http:// address.
Content-Security-PolicyGives the browser an allowlist of sources for the page's code and content.A policy built from the resources your pages really use, without broad script exceptions.Missing: medium. The value is shown but not reviewed.
X-Frame-Options · frame-ancestorsStops other sites from showing your page inside a frame of theirs.frame-ancestors in your CSP, with X-Frame-Options as a fallback for older browsers.Neither present: medium.
X-Content-Type-OptionsMakes the browser trust the declared content type of each response.The single value nosniff.Missing or any other value: low.
Referrer-PolicyLimits which part of your page address other sites see when visitors follow your links.A policy at least as strict as the current browser default.Missing: low.
Access-Control-Allow-OriginDecides which other sites' scripts may read your responses.Not sent on ordinary pages, or naming one trusted origin.A wildcard (*): medium.
Server · X-Powered-ByAnnounces the software behind the response.Removed, or reduced to a generic value.Any Server or X-Powered-By header: low, even without a version number. Only recognised technology names are shown.
Set-CookieSets cookies; its attributes decide which scripts can read them and when they are sent.Session cookies hidden from scripts and sent over HTTPS only.A cookie on this response without HttpOnly, or without Secure on HTTPS: medium. SameSite is not checked, and cookie values are never shown.

Permissions-Policy and Cross-Origin-Opener-Policy are not rated on this page. The launch readiness snapshot counts them only in its separate hardening grade.

How to fix

How to add these headers on your server

Set each header in one layer that answers every request, such as the web server, the CDN or the hosting platform. Start with the values below, then tighten them in stages while you test your key pages.

Safe starting values

  1. 01Strict-Transport-Security: max-age=300Send it on HTTPS responses only; browsers ignore it over plain HTTP. Raise max-age in steps, for example to one week, one month and then one year (31536000), and check your site at each step. Add includeSubDomains only when every subdomain serves HTTPS, and preload only after reading the hstspreload.org requirements, because leaving the preload list takes months.
  2. 02Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'Report-Only blocks nothing: the browser reports what the policy would block, in the console or to a reporting endpoint if you add one. Open your key pages, add the sources they really use, then send the same policy as Content-Security-Policy. Avoid 'unsafe-inline' for scripts; nonces or hashes are the stricter option.
  3. 03frame-ancestors 'self' · X-Frame-Options: SAMEORIGINframe-ancestors in an enforced Content-Security-Policy decides which sites may frame your pages. X-Frame-Options covers older browsers and does the blocking while your CSP is still in Report-Only. Use 'none' and DENY if your pages are never framed, keep both values in step, and send frame-ancestors as a header, because browsers ignore it in a meta tag.
  4. 04X-Content-Type-Options: nosniffSend the single value nosniff on every response. Browsers then refuse scripts and stylesheets served with the wrong Content-Type, so check first that your server labels JavaScript and CSS files correctly.
  5. 05Referrer-Policy: strict-origin-when-cross-originOther sites receive only your origin, never the full page address, and nothing when a link leads from HTTPS to HTTP. Current browsers already default to it; sending it keeps the behaviour the same everywhere. Choose same-origin or no-referrer if your URLs carry private data.
  6. 06Permissions-Policy: camera=(), microphone=(), geolocation=()An empty list () switches a feature off for your page and every frame it embeds. List only features your site never uses, then test pages with maps, video calls or payment widgets.

Copy-ready examples by platform

Every example sets the same starting values. Hosting platforms may already send Strict-Transport-Security, so the Vercel, Netlify and Next.js examples leave it out; check the live response and add it only if it is missing.

nginx

Put the lines in the server block that serves HTTPS. The always parameter adds them to error responses too. A location block with add_header lines of its own drops every add_header inherited from the server block, so repeat the set there or keep it in one include file.

# In the server block that serves HTTPS (listen 443 ssl)
add_header Strict-Transport-Security "max-age=300" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" 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=()" always;

Apache

Enable mod_headers and add the lines to the virtual host for port 443, or to .htaccess where AllowOverride FileInfo is set. Header always set also covers error responses and redirects.

# <VirtualHost *:443> or .htaccess; needs mod_headers (Debian/Ubuntu: a2enmod headers)
Header always set Strict-Transport-Security "max-age=300"
Header always set Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Header always set X-Frame-Options "SAMEORIGIN"
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=()"

Cloudflare

In the dashboard, create a Response Header Transform Rule for your hostname and add each header with the Set static operation, which replaces a header of the same name from your origin. HSTS has its own setting under SSL/TLS, Edge Certificates; use it or the rule, not both. Menu names can differ between dashboard versions.

Rule type   Response Header Transform Rule
When        Hostname equals example.com
Then        Set static, one row per header:
  X-Content-Type-Options               nosniff
  Referrer-Policy                      strict-origin-when-cross-origin
  X-Frame-Options                      SAMEORIGIN
  Permissions-Policy                   camera=(), microphone=(), geolocation=()
  Content-Security-Policy-Report-Only  default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'

HSTS        SSL/TLS > Edge Certificates > HTTP Strict Transport Security (HSTS)

Vercel

Add a headers entry to vercel.json in the project root. The source pattern /(.*) matches every path; narrow it if some routes need a different policy.

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
        { "key": "X-Frame-Options", "value": "SAMEORIGIN" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
      ]
    }
  ]
}

Netlify

Create a file named _headers in the publish directory, the folder Netlify deploys. Each path line, such as /*, is followed by indented Name: value lines. The same rules can also go in netlify.toml under [[headers]].

/*
  Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

Next.js

Return the headers from the async headers() function in next.config.js; the source '/:path*' covers every route. headers() does not run for a static export (output: 'export'), so set the headers on the host that serves the exported files instead.

// next.config.js
const securityHeaders = [
  { key: 'Content-Security-Policy-Report-Only', value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];

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

After you deploy, run the checker again: it reads the final response, so it shows what your CDN or platform really sends. Then open an error page and a static file in your browser's developer tools, because headers are often set for some responses only.

Reading the result

How to read each status.

  1. 01PassedThe header was found and meets the presence check.
  2. 02Low · Medium · HighSomething is missing or set in a way this check flags. The level is the same one the full snapshot uses.
  3. 03ObservedA value was recorded, but the check produced neither a pass nor a finding.
  4. 04Nothing observedThe check ran but there was nothing to record, for example no cookie on the response.
  5. 05Not applicableThe check does not apply, for example HSTS when you enter an http:// address.
  6. 06Not measuredNo usable response was received, so the check is not counted as passed.
Scope

What this checker does not do.

  1. 01It requests only the home page of the origin you enter. Paths and query strings are dropped, and no other page is requested.
  2. 02It mostly checks whether a header is present, not whether its value suits your site.
  3. 03It tests the HTTP to HTTPS redirect only when you enter the http:// address; that run skips TLS and does not report a missing HSTS header.
  4. 04It is not a vulnerability scan or a penetration test, and it sends no attack traffic.
  5. 05The result is a point-in-time view of one response; a CDN rule or another page can send different headers.
FAQ

Questions about the headers check.

Why is Server: nginx flagged without a version?

Any Server or X-Powered-By header is rated low, because the name alone tells visitors which software runs the site. Remove the header or reduce it to a generic value if your platform allows it.

Why does the result differ from my server configuration?

The checker reads the response it receives after up to five redirects. Headers that a CDN, proxy or hosting platform adds or removes are the ones that count, and CDN, proxy or bot-protection rules can answer browsers differently than they answer the checker.

Can I check a site I do not own?

Yes. The header check reads one ordinary response that the site already sends to every visitor; the full run sends a few more ordinary requests for the other checks. Protected modules and full audits require a verified target or explicit authorization.

Is this a vulnerability scan?

No. It is a passive check of response headers, not a vulnerability scan or a penetration test, and it does not prove that a site is secure.

Why are my headers missing on some responses?

Headers are often set for one route, file type or status code only. In nginx, add_header without always skips error responses, and a location block with add_header lines of its own drops the ones from the server block. Static files, API routes and pages served by another service can also bypass the rule that sets them.

Can I set the same header in the app and on the CDN?

Set each header in one layer. If two layers both add it, the browser can receive two values: with Content-Security-Policy, every policy sent applies, so the result is stricter than either one, and with Strict-Transport-Security only the first header is used.

Should I submit my domain to the HSTS preload list?

Only when every subdomain, now and later, can serve HTTPS. The list requires a max-age of at least one year with includeSubDomains and preload; browsers then use HTTPS for the domain even before the first visit, and removal can take months to reach users. A long max-age without preload already covers returning visitors.