Skip to content

free_tool

Is your next.config production-ready?

An app that builds fine can still ship with no security headers, a secret baked into the client bundle, type errors silenced at build, and an image optimizer anyone can use as a proxy. Paste your next.config and get a graded report with the exact line to change for each gap.

The config you paste runs entirely in your browser. It is never uploaded, sent to a server, or stored. (Anonymous usage metrics, never your config text, are sent to analytics.)

F

Next.js config health

7/100

5 to fix · 2 warnings · 0 passed · 1 note

Grade F, score 7 out of 100, 5 to fix, 2 warnings, 0 passed.

Security headers are set in headers()

high

No headers() function in the config, so Next.js sends no HTTP security headers of its own. Without a Content-Security-Policy the app is open to injected-script XSS, without Strict-Transport-Security a first request can be downgraded to HTTP, and without X-Content-Type-Options browsers may MIME-sniff responses. Add a headers() block (or set them at your CDN/middleware) covering CSP, HSTS, X-Content-Type-Options, Referrer-Policy and a frame policy.

async headers() {
  return [{ source: '/(.*)', headers: [
    { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
    { key: 'X-Content-Type-Options', value: 'nosniff' },
    { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
    { key: 'Content-Security-Policy', value: "default-src 'self'" },
  ]}];
}

Secrets aren't inlined into the client bundle via env

high

Secret-looking keys are in the next.config `env` block: STRIPE_SECRET_KEY, NEXT_PUBLIC_API_TOKEN. Values in the config `env` block are inlined into the JavaScript bundle at build time, so anything there ships to every visitor's browser in plain text. Server-only secrets must never go through `env`. Read them directly from process.env in server code (Server Components, route handlers, getServerSideProps) and drop them from the config.

// remove from next.config env; read process.env.API_KEY in server code only

No secret-shaped NEXT_PUBLIC_ variables

high

Secret-looking NEXT_PUBLIC_ variable(s) referenced: NEXT_PUBLIC_API_TOKEN. The NEXT_PUBLIC_ prefix tells Next.js to embed the value in the browser bundle, so a NEXT_PUBLIC_ name containing SECRET/KEY/TOKEN/PASSWORD publishes that credential to every visitor. If it's truly public (a publishable/anon key), the name is just alarming; if it's a real secret, rename it without NEXT_PUBLIC_ and use it only on the server.

// drop the NEXT_PUBLIC_ prefix; use the value in server code via process.env

Type and lint errors fail the build

high

typescript.ignoreBuildErrors is set to true, so production builds ship even with type errors. typescript.ignoreBuildErrors in particular means a real type bug (a null deref, a wrong shape) reaches production instead of failing the build. Turn it off and fix the errors; the whole point of TypeScript is that this gate catches them.

typescript: { ignoreBuildErrors: false }

Image optimizer isn't an open proxy

medium

An image host pattern is a bare wildcard (** / *), which turns the Next.js image optimizer into an open proxy: anyone can pass any URL through /_next/image, using your server's bandwidth and letting it fetch internal or arbitrary hosts (an SSRF vector). Restrict remotePatterns to the exact hostnames you serve images from.

images: { remotePatterns: [{ protocol: 'https', hostname: 'cdn.yoursite.com' }] }

Production source maps aren't served publicly

medium

productionBrowserSourceMaps is true, so full, readable source maps are served in production. That hands anyone your original source (comments, internal names, and any secret accidentally hardcoded client-side) and enlarges the deploy. Leave it off unless you're actively debugging prod, and prefer uploading source maps privately to your error tracker instead.

productionBrowserSourceMaps: false

Strict mode on, framework not advertised

low

reactStrictMode is explicitly false, which disables the extra development checks that surface unsafe lifecycles, side-effect bugs and deprecated APIs. Unless a specific library forces it off, turn it back on so those bugs show up in dev instead of production. Also set poweredByHeader: false to stop advertising the framework in the X-Powered-By header.

reactStrictMode: true,
poweredByHeader: false

Output mode matches the deploy target

No output mode set, which is right for Vercel and most managed hosts. If you deploy to a container (Cloud Run, Fly, a VM), set output: 'standalone' to ship a small self-contained server bundle instead of the whole node_modules.

A clean config is the floor, not the ceiling. The CSP that's actually restrictive, the secrets that never reach the bundle, and the caching that decides your bill and your speed are where a Next.js app is won or lost. That's the kind of review I do.

Get your Next.js app production-ready: book a call

Static analysis of the config text only. There is no JavaScript parser bundled, so it scans for the specific keys and header names it grades (the headers() block, the env object, typescript/eslint build valves, images.remotePatterns / domains, productionBrowserSourceMaps, poweredByHeader, reactStrictMode and output). It runs entirely in your browser and uploads nothing.

why_it_matters

next.config is where a Next.js app leaks and breaks

The most common Next.js production incidents start in this one file. Values in the env block are inlined into the browser bundle, so a secret there is published to every visitor. A wildcard image host turns /_next/image into an open proxy and an SSRF vector. ignoreBuildErrors ships type bugs to production, and a missing headers() block leaves the app with no CSP or HSTS.

This auditor encodes those rules so the expensive mistakes get caught in a config review instead of an incident, and points at the exact line to change for each one.

faq

Questions & answers

What does the Next.js Config Auditor check?
It parses your next.config.js / .mjs / .ts and grades it on the footguns that actually bite Next.js apps in production: whether a headers() block sets the core security headers (Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options and more), whether the env block or a NEXT_PUBLIC_ name leaks a secret into the client bundle, whether typescript.ignoreBuildErrors or eslint.ignoreDuringBuilds silence the build gate, whether an images host pattern is a wildcard that turns the optimizer into an open proxy, whether productionBrowserSourceMaps publishes your source, and the poweredByHeader and reactStrictMode hygiene toggles. Each finding gives the exact line to change.
Why is a secret in the next.config env block dangerous?
Values you put in the next.config `env` block are inlined into the JavaScript bundle at build time, so they are shipped to every visitor's browser in plain text. That's fine for genuinely public configuration, but a server-only secret (a Stripe secret key, a database URL, an API token) placed there is effectively published. The fix is to read server-only secrets directly from process.env in server code (Server Components, route handlers, getServerSideProps) and never route them through the config `env` block or a NEXT_PUBLIC_ variable.
What's wrong with a wildcard hostname in images.remotePatterns?
A remotePatterns entry with hostname '**' (or the deprecated images.domains holding '*') lets the Next.js image optimizer fetch and re-serve any URL through /_next/image. That turns your server into an open image proxy: attackers can burn your bandwidth, and because your server makes the outbound request, it can be pointed at internal or arbitrary hosts, which is a server-side request forgery (SSRF) vector. Scope remotePatterns to the exact hostnames (and ideally protocol and path prefix) you actually serve images from.
Should I ever set typescript.ignoreBuildErrors to true?
Almost never for production. typescript.ignoreBuildErrors: true tells Next.js to build even when TypeScript reports errors, so a real bug (a null dereference, a wrong data shape, a renamed field) reaches production instead of failing the build. The build-time type check is the main safety benefit of TypeScript; disabling it throws that away. If you're mid-migration, fix the errors or suppress them narrowly with // @ts-expect-error rather than turning off the whole gate. The auditor treats a global ignore as a high-severity fail.
Does this replace a live security-headers scan?
No, it complements one. This tool reads your config source and tells you which security headers you declare in headers(); it can't see headers you set at a CDN, load balancer, or in middleware, and it can't confirm the header actually reaches the browser. Pair it with a live check like the Security Headers Analyzer, which fetches a deployed URL and grades the headers that are actually served. The config auditor catches the gap before deploy; the live scan confirms it after.
Is my config uploaded anywhere?
No. The config you paste is analyzed entirely in your browser with plain JavaScript. There is no JavaScript parser bundled, so it scans for the specific keys and header names it grades rather than executing your config. Nothing you paste is uploaded, sent to a server, or stored; only anonymous usage metrics are sent to analytics.

Want the rest of the app looked at?

The config is the floor. I'll review the CSP that's actually restrictive, the data-fetching and caching that decide your bill and your speed, and the rendering strategy that decides your Core Web Vitals. Book a call, or leave your email.

Book a call

No spam. You'll get a reply from me.

Prefer proof first? See how this plays out in real case studies →