All posts

Field guide

HTTP security headers: the ones that matter and how to set them

There are a dozen security headers you could set, and most guides list all of them without saying which are worth your time. Here are the ones that close real gaps, what each actually stops, and where to put them.

Last updated

HTTP security headers are instructions your server sends with every response telling the browser how to behave more safely: force HTTPS, refuse to be framed, restrict what scripts can run. They are one of the cheapest security wins available, because most of them are a few lines of config. The catch is that the standard advice lists everything, including headers that no longer do anything. Here is the short list that matters.

Strict-Transport-Security (HSTS)

Forces browsers to use HTTPS for your domain, even if a user types http://. This closes the window where a first insecure request can be intercepted. Set it once you are confident every subdomain is on HTTPS:

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

Start with a shorter max-age while testing, then raise it. The includeSubDomains flag is powerful, so make sure no subdomain still needs plain HTTP before you add it.

Want to see exactly what Prbl flags? Watch it scan a demo app, no repo or account needed.

See a live scan →

Content-Security-Policy (CSP)

The most powerful and the most finicky. A CSP tells the browser which sources it may load scripts, styles, images, and frames from, which sharply limits cross-site scripting. A reasonable starting point:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; frame-ancestors 'none'

Because a CSP can break your own site if it is stricter than what you load, roll it out with Content-Security-Policy-Report-Only first, watch what gets flagged, then enforce. The full walkthrough is in setting a CSP in Next.js.

The quick wins

Three more that are safe to set without much testing:

X-Content-Type-Options: nosniff            # stop MIME-type guessing
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: DENY                       # or use frame-ancestors in CSP

nosniff stops the browser from second-guessing your content types, Referrer-Policy limits what you leak in the Referer header, and X-Frame-Options (or a frame-ancestors CSP rule) stops your pages being framed for clickjacking.

Where to set them (Next.js example)

// next.config.js — applied to every response
module.exports = {
  async headers() {
    return [{
      source: "/:path*",
      headers: [
        { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" },
        { key: "X-Content-Type-Options", value: "nosniff" },
        { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
        { key: "X-Frame-Options", value: "DENY" },
      ],
    }];
  },
};

Behind Nginx these become add_header directives; on a static host they go in your platform config file. The rule is the same: the header must be on the response the browser actually receives.

What you can skip

Drop X-XSS-Protection — it is deprecated and modern browsers ignore it, and a bad value can even reintroduce issues. You also do not needFeature-Policy; it was renamed to Permissions-Policy, which is worth adding only if you actually want to lock down camera, microphone, or geolocation access.

Check what your site sends today

Missing headers are invisible until someone checks the response. Our free security headers checker grades your site’s headers and gives you a copy-ready value for each one you’re missing. For the app itself — exposed keys, open databases — run a free scan.

Frequently asked questions

Which security headers actually matter?

The high-value ones are Strict-Transport-Security (force HTTPS), Content-Security-Policy (limit what can load and run), X-Content-Type-Options (stop MIME sniffing), and a sensible Referrer-Policy and X-Frame-Options or frame-ancestors (stop clickjacking). A few older headers like X-XSS-Protection are now deprecated and can be dropped. Focus on HSTS and CSP first, since they close the widest gaps.

Do I need all of them?

No. Strict-Transport-Security and a Content-Security-Policy give you most of the protection. X-Content-Type-Options and a frame-ancestors rule are quick wins on top. Do not add headers you do not understand, especially a strict CSP, without testing, because a wrong CSP can silently break your own scripts and styles. Add them incrementally and check the site still works after each.

Where do I set security headers?

Wherever your responses are served. In Next.js that is the headers function in next.config, or middleware. On Vercel or Netlify it is a config file. Behind Nginx it is add_header directives, and behind a CDN like Cloudflare you can set some at the edge. The header has to be on the actual response the browser receives, so set it as close to what serves your HTML as possible.

Why is my Content-Security-Policy breaking my site?

Almost always because the policy is stricter than what your app actually loads. If you block inline scripts but your app uses them, or you forget to allowlist a third-party domain you depend on, those resources stop loading. The fix is to start in report-only mode, watch what gets flagged, and widen the policy to cover your real dependencies before you enforce it.

Prbl scans your live app or your codebase for exactly the kinds of issues above.

HTTP Security Headers: The Ones That Matter and How to Set Them: Prbl