Skip to content
Sandeep Kumar ChaudharySandeep
Back to BlogDevOps

Security Headers for Next.js Behind Cloudflare

By Sandeep Kumar ChaudharyOct 10, 20263 min read
Padlock symbol representing website security

TL;DR

Six response headers set once in next.config.ts cover the common gaps on a content site: HSTS, nosniff, Referrer-Policy, X-Frame-Options, Permissions-Policy and COOP. Two Cloudflare settings complete it: a minimum TLS version of 1.2 and an edge redirect from HTTP to HTTPS.

Key takeaways

  • Set headers in one place, the headers() function in next.config.ts, so every route gets them.
  • Start HSTS without preload. Preloading is hard to undo.
  • COOP same-origin breaks windows a page opens, such as ad click-throughs. Use same-origin-allow-popups when you run ads.
  • Check headers with curl through Cloudflare, not just against the origin.

A security audit of this site found a handful of missing response headers. None of them is dramatic on its own, but together they close off several easy attacks. This post lists what I set, why, and the one header I had to loosen once ads went live.

The site is a Next.js app behind Cloudflare. Everything below is set in one place, so every route gets it.

Setting headers once in Next.js

Next.js lets you attach headers to every route from next.config.ts:

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: "SAMEORIGIN" },
        { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" },
        { key: "Cross-Origin-Opener-Policy", value: "same-origin-allow-popups" },
      ],
    },
  ];
},

What each header does

Strict-Transport-Security (HSTS) tells browsers to use HTTPS for this domain for the next year, even if someone types http:// or follows an old link. It stops a network attacker from quietly downgrading a visitor to plain HTTP. I left out the preload flag on purpose. Preloading writes your domain into browsers' built-in lists, and getting it removed takes months.

X-Content-Type-Options: nosniff stops browsers from guessing a file's type. Without it, a browser might treat an uploaded text file as a script.

Referrer-Policy: strict-origin-when-cross-origin controls what other sites learn when a visitor clicks a link away from yours. They get the domain only, never the full path or query string.

X-Frame-Options: SAMEORIGIN prevents other sites from loading your pages in a frame. That blocks clickjacking, where an invisible copy of your page is layered over another site to trick clicks.

Permissions-Policy declares that the site never uses the camera, microphone, location or payment APIs. If a third-party script ever tries, the browser refuses.

Cross-Origin-Opener-Policy (COOP) isolates the page from windows that open it or that it opens.

The header I had to loosen

I first set COOP to same-origin, the strictest value. It cuts the connection between your page and any window it opens.

That is a problem once you run ads. When a reader clicks an ad, the ad code opens the advertiser's site in a new window and may need to keep a reference to it. With same-origin, that reference is severed. When I added Google AdSense, I changed the value to same-origin-allow-popups. It still isolates the page from sites that open it, but keeps windows the page itself opens working.

If you run ads, payment pop-ups or "sign in with" pop-ups, use same-origin-allow-popups.

Two Cloudflare settings that complete it

Some protections belong at the edge, where Cloudflare terminates the connection.

Minimum TLS version: 1.2. Cloudflare's default accepted TLS 1.0 and 1.1, which are deprecated. Raising the minimum to 1.2 in SSL/TLS settings refuses those old versions. Every current browser supports 1.2 or 1.3, so no real visitor is affected.

Always Use HTTPS. This makes Cloudflare redirect plain HTTP requests to HTTPS at the edge, before they reach your server. I keep a matching redirect in Next.js as a fallback. It checks Cloudflare's CF-Visitor header, which records the scheme the visitor actually used, so the two never create a redirect loop.

Verify from outside

Always check what visitors actually receive, through Cloudflare, not just what your server sends:

curl -sI https://sandeepkumarchaudhary.com/ | grep -iE "strict-transport|nosniff|referrer|x-frame|permissions|cross-origin"

To confirm the old TLS versions are refused:

curl -s -o /dev/null -w "%{http_code}\n" --tlsv1.1 --tls-max 1.1 https://sandeepkumarchaudhary.com/

A result of 000 means the connection was refused, which is what you want.

What I left out

A strict Content-Security-Policy is the strongest defence against cross-site scripting. Next.js injects inline scripts, so a strict policy needs per-request nonces threaded through the framework. That is worth doing, but as its own project with testing, not as a header pasted into a config file.

Security headers are cheap to add and easy to forget. Set them in one place, test them through your CDN, and revisit them whenever you add a third-party service.

#Security#Next.js#Cloudflare#HTTP headers

Frequently Asked Questions

Does Cloudflare strip headers set by my origin?

No. Cloudflare passes response headers from your origin through to visitors. Check with curl -I against your public domain to confirm what visitors actually receive.

Should I add a Content-Security-Policy?

A strict CSP is the strongest protection against cross-site scripting, but Next.js injects inline scripts, so a strict policy needs nonces wired through the framework. Treat it as a separate project rather than a one-line header.

Why not enable HSTS preload right away?

Once a domain is in browsers' preload lists, removing it takes months. Run HSTS without preload first and add it only when you are sure every subdomain will serve HTTPS permanently.

Sandeep Kumar Chaudhary

Sandeep Kumar Chaudhary

Full Stack Software Developer· Nepal's SEO, AEO, GEO & AIO expert and share-market educator. More about me