Sourcemap Explorer
Guide

How to check if a website is built with Next.js

Next.js is built in a way that makes it hard to hide. The framework inserts a `#__next` wrapper, a `__NEXT_DATA__` JSON script, and loads every route chunk from `_next/static/`. One glance at DevTools is enough to confirm that a site is Next.js, and a second glance with Sourcemap Explorer is enough to read the exact version, the router (Pages vs App), and whether the page was server-rendered, statically generated, or revalidated through ISR.

By Mapree ·

4 min readChrome DevTools, Sourcemap Explorer extension

Background

Next.js has two active router paradigms (Pages Router, around since v9; App Router, since v13) and both leave distinctive traces in the page's output. Pages Router apps are easy — `#__next` wrapper, `__NEXT_DATA__` JSON script, `/_next/static/chunks/pages/_app-<hash>.js` script URL. App Router apps are slightly trickier: no `#__next` wrapper, no `__NEXT_DATA__`, just RSC flight payloads pushed via `self.__next_f.push(...)` and chunks under `/_next/static/chunks/app/`. Both share the `/_next/static/` prefix, which is the most reliable single signal and the one fingerprint that cannot be stripped without breaking the app.

Layered on top of those DOM/asset signals are HTTP headers — `X-Powered-By: Next.js`, `X-Nextjs-Cache`, `X-Nextjs-Matched-Path`, `X-Nextjs-Stale-Time`, sometimes a `X-Nextjs-Prerender-Bypass` cookie. Self-hosted Next deployments tend to keep most of these; deployments behind aggressive CDNs or hardening proxies sometimes strip the `X-Powered-By` line for security-by-obscurity reasons but leave the rest. The `X-Nextjs-Cache` value in particular is informative: `HIT` means an ISR-cached response, `MISS` means server-rendered on this request, `REVALIDATED` means the cache was refreshed for you, `STALE` means you got the previous version while the cache regenerates in the background. None of those values is something a non-Next framework would emit by accident.

Getting the exact version takes one more step. The `X-Powered-By` header sometimes includes a version (`X-Powered-By: Next.js 14.2.3`), but on many production deployments it is stripped or carries only the framework name. The reliable path is the sourcemap: when the site ships JavaScript sourcemaps (the default for any Next.js build that has not explicitly disabled them), `node_modules/next/package.json` is one of the source files referenced inside the map, and its `version` field is the exact semver of Next.js that built the bundle. Sourcemap Explorer reads that automatically and surfaces it in the popup, which is the single fastest way to go from 'is this Next.js' to 'this is Next.js 14.2.3 with App Router'.

Why this matters

Next.js is one of the most-used production React frameworks, and the variant of Next.js running on a site tells you a lot about how it should be tested, optimized, and integrated with. Knowing a site is on Next.js tells you it's SSR-capable, likely hosted on Vercel (but not necessarily), probably using a recent React, likely paired with Tailwind and shadcn/ui, and subject to specific ISR/streaming behaviors that affect how you should think about its performance. It also narrows down what else to look for — NextAuth, Prisma, tRPC, SWR, TanStack Query, Vercel Analytics, Edge Config — are all common companions.

The router variant matters more than people expect. App Router apps are RSC-first, which means much of the rendering happens on the server and the client bundle ships only the interactive portions; performance characteristics, hydration cost, and the right way to test for layout shift are all different from Pages Router apps. App Router also unlocks streaming, partial prerendering and the `loading.tsx` boundary pattern, all of which a thoughtful audit should account for. Telling Pages Router from App Router in the first thirty seconds saves you from writing the wrong recommendations later.

For sales, partnerships and integrations work the version itself is load-bearing. 'They are on Next 13' implies they are still on the Pages Router or just ramping up App Router; 'they are on Next 14' implies App Router stable and Server Actions GA; 'they are on Next 15' implies React 19 in the mix and a different set of upgrade considerations. Treating Next.js as one undifferentiated thing misses the fact that the upgrade cliff between consecutive majors is real and often the deciding factor in whether an integration ships this quarter or next.

Prerequisites

  • Chrome DevTools access — the workflow uses the Elements panel and Network tab, both available in any Chromium browser.
  • Basic familiarity with the Network and Elements panels: opening DevTools, switching tabs, filtering requests, inspecting headers.
  • Optional: Sourcemap Explorer installed, which collapses the multi-step manual workflow into a single popup click and adds the exact-version reading via sourcemap parsing.
  • Optional: knowledge of the Pages-vs-App router distinction in Next.js — useful context for interpreting what you find, but the workflow surfaces it without prior knowledge.

Step-by-step

  1. 1

    Check for #__next in the DOM

    Open DevTools (`F12` or `Ctrl+Shift+I`), switch to the Elements panel, and search for `__next`. If you see `<div id="__next">` (or occasionally `<div id="__next__">` on older versions), the site is a Next.js Pages Router app — that wrapper is one of the framework's most stable fingerprints and has been there since the early v9 days. If the search returns nothing the site is either App Router or not Next at all; the next two steps will tell you which.

  2. 2

    Look for __NEXT_DATA__ script

    In the Elements panel, search for `__NEXT_DATA__`. On Pages Router apps you'll find a `<script id="__NEXT_DATA__" type="application/json">` element near the bottom of `<body>` carrying serialized page props, the build ID, the route, and a structured representation of the request. On App Router apps that script is absent and the equivalent payload is streamed instead — search for `self.__next_f.push` in inline scripts to see the RSC flight chunks. Either pattern is a strong Next.js confirmation; together with the `_next/static/` asset path they constitute essentially undeniable evidence.

    Tip: `__NEXT_DATA__` has a `buildId` field — a hash of the current deployment. Different `buildId`s on reload means the site was redeployed between your visits, which is occasionally useful diagnostic context (and the App Router equivalent is the build hash embedded in the chunk filenames).

  3. 3

    Inspect asset URLs in the Network tab

    Open the Network tab, filter by 'JS' or 'CSS', and reload. Every Next.js app loads chunks from `/_next/static/chunks/` and stylesheets from `/_next/static/css/`. If you see those paths it's Next.js. The chunk subdirectory tells you the router: `/_next/static/chunks/pages/` (Pages Router) vs `/_next/static/chunks/app/` (App Router); some App Router apps also load from `/_next/static/chunks/main-app-<hash>.js`. The `_next` prefix is itself a hard fingerprint — it's hard-coded in the framework and cannot be renamed without forking Next.

    Tip: Many CDNs (Cloudflare, AWS CloudFront) cache `_next/static/` aggressively because the chunks have content-hash filenames and are immutable. Long `Cache-Control: public, max-age=31536000, immutable` headers on those URLs are a Next.js trademark.

  4. 4

    Check response headers on the document request

    Click the very first request in the Network tab (the document HTML), open the 'Headers' panel, and scan the response headers. `X-Powered-By: Next.js` is the easy giveaway and is often accompanied by `X-Nextjs-Cache`, `X-Nextjs-Matched-Path`, and on Vercel `X-Vercel-Id` and `X-Vercel-Cache`. The cache header values are informative: `HIT` means ISR served from cache, `MISS` means server-rendered on this request, `REVALIDATED` means the cache was refreshed for you, `STALE` means you got the previous response while regeneration runs in the background.

    Tip: `X-Nextjs-Cache: HIT` indicates an ISR-cached page; `MISS` means server-rendered on the request; `REVALIDATED` means the cache was refreshed for this request; `STALE` means you got the previous version while a fresh one regenerates. Together with the route in `X-Nextjs-Matched-Path`, these tell you exactly how the page is being served.

  5. 5

    Get the exact version via Sourcemap Explorer

    If the site ships sourcemaps (most Next.js apps do by default — Next exposes `.map` files for client chunks unless explicitly disabled), Sourcemap Explorer will read `node_modules/next/package.json` from the map and show the exact version — e.g. Next.js 14.2.3 — instead of the coarse major number some detectors report. The popup also infers the React version from the same source ('React 18.2.0' or 'React 19.0.0'), which is the second most useful piece of metadata after the Next version itself.

  6. 6

    Confirm the rendering mode

    Reload the page with DevTools open, watch the document request, and read the response. If the HTML body contains real content (not just a `<div id="__next"></div>` shell), the page was server-rendered or statically generated — Next did the work before sending you the HTML. If the body is essentially empty and content materializes via JavaScript after the load, you are looking at a client-side-rendered page (rare in Next.js but possible for fully dynamic routes). The `X-Nextjs-Cache` header value clarifies the static-vs-server-rendered question on the document itself.

Real-world example

Alternative methods

Check via the View Source HTML

If you do not have DevTools open, `Ctrl+U` shows the raw response HTML. Search for `__NEXT_DATA__`, `_next/static/`, or `__next` to confirm Next.js. This works on any browser and on devices where DevTools is locked down (kiosks, work-issued laptops with restrictive policies). The trade-off is you cannot read response headers from View Source.

Probe with curl

From a terminal, `curl -I https://<site>/` returns just the response headers. Look for `X-Powered-By: Next.js`, any `X-Nextjs-*` header, or the absence of either. `curl -s https://<site>/ | grep '_next/static'` confirms the asset path. Useful when you want to script the check across many domains, although it cannot detect App Router-only sites that strip headers.

Use a public detector site

Tools like BuiltWith, Wappalyzer or W3Techs can identify Next.js from the same fingerprints, but they generally do not have access to source maps and so cannot reach the exact version the way Sourcemap Explorer does. If you only need the binary 'is this Next.js' question and you cannot install an extension, the public detectors are fine.

Troubleshooting

I see `_next/static/` but no `__NEXT_DATA__`.

You're on an App Router page. Look for `self.__next_f.push(...)` in inline scripts — that's the RSC flight payload, which replaces `__NEXT_DATA__` in App Router. The chunk subdirectory `/_next/static/chunks/app/` confirms it.

No `X-Powered-By` header.

Admin-stripped for security theater, or fronted by a CDN that drops headers. The `_next/static/` path prefix is your confirmation; it can't be stripped without breaking the app. The `X-Nextjs-Cache` header often survives even when `X-Powered-By` does not.

Sourcemap Explorer can't find a Next.js sourcemap.

The site disabled sourcemap output in production (set `productionBrowserSourceMaps: false` or did not enable the equivalent in `next.config.js`). You can still confirm the framework via the DOM/network signals, but the exact version requires the `X-Powered-By` header — open DevTools, look at the document headers.

I see `__NEXT_DATA__` but the bundle is tiny and no chunks under `_next/static/`.

Almost certainly a static export served from a non-Next host. The `__NEXT_DATA__` shell ships with the export but the runtime chunks may be inlined or served from a custom path. Use the `_next` reference in any asset URL (HTML, CSS, image) as your confirmation.

The page shows up as Next.js but I'm getting weird hydration errors.

That is a separate problem (data mismatch, stale ISR, or component-level issue) and has nothing to do with the detection step. Open the React DevTools or check the console for the actual hydration mismatch error; the framework identification is solid.

Caveats

What to do next

Once you've confirmed Next.js, the natural follow-ups are getting the exact React version (App Router on Next 15 is React 19, otherwise expect React 18.x), detecting any CMS behind it (headless WordPress via WPGraphQL, Sanity, Contentful, Payload, Storyblok), and enumerating the UI kit and data layer (Tailwind, shadcn/ui, Radix UI, TanStack Query, SWR, server-only TRPC). The [find-the-exact-version-of-an-npm-package-on-a-site](/how-to/find-the-exact-version-of-an-npm-package-on-a-site) and [see-every-javascript-library-a-site-uses](/how-to/see-every-javascript-library-a-site-uses) guides cover those as direct extensions of the Next.js detection workflow. If you suspect a headless WordPress backend, [identify-the-cms-behind-any-site](/how-to/identify-the-cms-behind-any-site) walks through the cross-checks (network requests to `/wp-json/wp/v2/` or `/graphql`, telltale headers from the WordPress backend, attribution of the `dateModified`-style metadata on individual pages). For performance work the version-level information is often the entry point — Next 13 vs 14 vs 15 each unlock different optimization paths, and pairing the version with the hosting platform (Vercel, self-hosted, AWS Amplify, Cloudflare Pages) tells you which knobs are realistic.

FAQ

Can I tell App Router from Pages Router?

Yes. Pages Router apps have `<div id="__next">` in the DOM and a `<script id="__NEXT_DATA__" type="application/json">` block; App Router apps have neither and instead stream RSC payloads via `self.__next_f.push(...)` in inline scripts. The chunk subdirectory in `/_next/static/chunks/` (`pages/` vs `app/`) is the other strong signal. Sourcemap Explorer surfaces this distinction directly when the sourcemap is available.

How do I tell what version of Next.js is running?

The `X-Powered-By` header sometimes includes the version (e.g. `Next.js 14.2.3`). When it does not, the reliable path is the sourcemap — `node_modules/next/package.json` inside the map carries the exact `version` field, and Sourcemap Explorer reads it automatically. For sites that ship no sourcemaps and strip the header, the major version can sometimes be inferred from feature signals (App Router presence implies 13+, React 19 implies 15+).

Is every Vercel site a Next.js site?

No. Vercel hosts many frameworks — Remix, SvelteKit, Astro, Nuxt, plus plain static sites. The `X-Vercel-Id` header only confirms hosting; the `_next/static/` chunk path confirms Next.js specifically. A site can be on Vercel without being Next.js, and a Next.js app can be hosted anywhere (AWS, Cloudflare Workers/Pages, self-hosted on Kubernetes, Netlify with the Next runtime adapter).

Can a site fake being Next.js?

Theoretically yes, but no one does. Spoofing `__NEXT_DATA__`, `_next/static/` paths, the right header surface and a sourcemap that includes `node_modules/next/package.json` would be more work than just running Next. The fingerprint combination is too deep and too consistent to forge by accident.

What does the `X-Nextjs-Cache: STALE` header value mean?

You received the previously cached version of an ISR page while Next regenerates the fresh version in the background. The next request will probably get `REVALIDATED` (the regeneration completed and the new version was served), then `HIT` for subsequent requests until the next revalidation cycle.

Does Next.js detection work on a localhost / dev build?

Yes, with the same signals — `_next/static/` paths, `__NEXT_DATA__`, the framework headers all behave identically on `localhost:3000`. Sourcemap reading also works because dev builds ship sourcemaps by default. This is occasionally useful when QA-ing your own site or a customer's preview deployment.

How do I tell which React version a Next.js site uses?

Sourcemap Explorer surfaces it from `node_modules/react/package.json` via the same sourcemap path used for the Next.js version. Failing that, App Router apps are React 18+ (Server Components require it) and Next 15 apps are React 19 by default. Pages Router apps can be on any React 17/18.

Related

Skip the manual steps.

Sourcemap Explorer automates every workflow in this guide — free, local, no sign-up.

Install free on Chrome