Stop 1 of 42 · The Front Door · Security headers

Signs missing at the front door

Every bank posts signs at the door: “No recording,” “Do not copy our windows.” Your server can post signs for the browser too. If the bank posts none, the browser falls back to loose defaults, and Mallory can build a fake booth around the bank’s window and trick customers into pressing real buttons.

Severity Low–HighCategory Missing Headers / Info DisclosureThe Front DoorSources checked 25 Oct 2026

Watch it happen

Play it as

The bank has no signs. Mallory frames the bank’s window behind a fake prize booth, and Alice unknowingly presses the bank’s Approve button.

Arrival

Alice walks up to a bank that has put up no signs at the door about who may show its windows.

1 / 5

In bank terms and app terms

In the bankIn your app
Signs posted at the doorHTTP response headers your server sends with every page
“Never show this window in another booth”X-Frame-Options: DENY or CSP frame-ancestors
“Only locked roads, please”Strict-Transport-Security (HSTS) forces HTTPS
“Only run the bank’s own scripts”Content-Security-Policy limits where scripts can load from
The browser reading the signThe browser enforcing the headers for visitors

Are you exposed?

You might be exposed if…
  • Your site is a Next.js, Express or Vite app and you have never configured response headers.
  • Running a header checker on your live URL shows missing Content-Security-Policy, Strict-Transport-Security or X-Frame-Options.
  • Your server answers with X-Powered-By: Express or a Server header that shows an exact version.
What to do about it
  1. Add the headers once for every route: headers() in next.config.js, helmet() in Express, or your host’s headers file.
  2. Start with Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or CSP frame-ancestors 'none') and a Referrer-Policy.
  3. Add a Content-Security-Policy such as default-src 'self', then loosen only what your app really needs. Remove X-Powered-By.

Track these in your to-do list →

What it looks like in code

// next.config.js
const securityHeaders = [
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
  { key: "Content-Security-Policy", value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" }, // add a nonce-based script-src if you need inline scripts
  { 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=()" },
]

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

Ask your AI tool to fix it

Paste this into Cursor, Claude, Lovable, Bolt or whatever you build with.

Prompt
Add security headers to every response in this app. Set Strict-Transport-Security (max-age=63072000, and includeSubDomains only if every subdomain already serves HTTPS), X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or CSP frame-ancestors), Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy that disables camera, microphone and geolocation, and a Content-Security-Policy that starts from default-src 'self'. Remove X-Powered-By and any version in the Server header. Then check that the site still works and tell me what I had to allow in the CSP.

Where this comes from

The facts, figures and fixes on this page were checked against these sources on 25 Oct 2026. They are linked so you can check for yourself.

Spot a mistake or something out of date? Report a correction · How these guides are made and checked