Sourcemap Explorer
Guide

How to download JavaScript sourcemaps from a website

A JavaScript sourcemap (`.map`) is a JSON file that maps every byte of a minified bundle back to its original source — with full path, full content, and often full `node_modules` paths and `package.json` files included as sources. When a team ships sourcemaps to production (most do, by accident or by choice), that file is effectively a zip of their `src/` folder. You just have to download it and unpack it.

9 min readChrome DevTools, curl, Sourcemap Explorer extension

Background

Sourcemaps exist because error trackers, debuggers and developer tools need a way to map minified production JavaScript back to readable original source. When an exception fires in production, Sentry, Datadog or Bugsnag need to show you `UserProfile.tsx:42` instead of `a.js:1:17293`. That translation layer requires the sourcemap file, which is a JSON document containing — among other things — the original path of every source file that was bundled and, optionally, the full original contents of those files. When a build tool like webpack, Vite, Rollup or esbuild includes `sourcesContent` in the map (the default for every major bundler), shipping a sourcemap to production means shipping the source code embedded inside the map. Most teams know this in the abstract and still ship them publicly — because uploading maps separately to a third-party error tracker is extra friction, because the security impact feels small compared to the debugging value, because hosting platforms like Vercel and Netlify emit maps by default without a second thought. The practical effect is that a very large portion of the modern web ships its source alongside its bundle. This guide covers how to find those maps, download them, and put the original project tree back together.

Why this matters

Reading a production frontend's actual source is a completely different experience from reading its minified bundle. Minified code tells you the runtime behavior; the source tells you the architecture, the component decomposition, the naming conventions, the commented-out experiments, the TODO notes. For learning from production code this is the difference between reading a transcript and reading a novel. For security research it's the difference between guessing at an endpoint and reading the route table. For competitive analysis it's the difference between 'they use React' and 'they use React 18.2 with an interesting router pattern and a specific state-management approach'.

Prerequisites

  • Chrome DevTools familiarity — especially the Network panel.
  • `curl` or a similar HTTP client for direct file fetches.
  • Node.js if you want to script the reconstruction step.
  • Disk space — production sourcemaps routinely exceed 20 MB each, and a full site can accumulate 100-500 MB across bundles.

Step-by-step

  1. 1

    Open DevTools and find a bundle

    Open DevTools (F12), go to the Network tab, filter by 'JS', and reload. Pick a reasonable-sized script — something like `main-<hash>.js` or `_app-<hash>.js`. Avoid the smallest chunks (likely lazy-loaded routes with minimal content) and the vendor bundle on the first pass — you want the application's own code if you can identify it.

    Tip: In Next.js apps, the most interesting application code lives in `/_next/static/chunks/pages/` (Pages Router) or `/_next/static/chunks/app/` (App Router). Start there.

  2. 2

    Look for the `SourceMap` response header

    Click the script request. In Headers → Response, look for `SourceMap:` or `X-SourceMap:`. If it's there, that's the URL of the map file — resolve it relative to the script. Headers are the cheapest possible indicator because the server explicitly advertises the map's location.

    Tip: If the header value is a relative URL, resolve it against the directory of the script, not the HTML page. `/assets/main-abc.js` with header `main-abc.js.map` becomes `/assets/main-abc.js.map`.

  3. 3

    Fall back to the sourceMappingURL comment

    If there's no header, download the script itself ('Response' tab → 'Save as' in newer Chrome, or fetch via `curl`), and look at the very last line. It'll often be `//# sourceMappingURL=main-<hash>.js.map`. That's the map URL, relative to the script. Prepend the script's directory to get the absolute URL.

    curl -s 'https://example.com/assets/main-abc.js' | tail -c 200

    Tip: For large scripts, use a Range request to download only the last few KB: `curl -H 'Range: bytes=-4096' ...`. That's what Sourcemap Explorer does internally to keep bandwidth low.

  4. 4

    Fetch the map file

    Request the map URL directly with curl or in the browser. You'll get a JSON blob with `version`, `sources`, `sourcesContent`, `names` and `mappings` fields. The `sources[]` array is the list of original file paths; `sourcesContent[]` is their original text, matched by index.

    curl -s 'https://example.com/assets/main-abc.js.map' -o main-abc.js.map
    jq '.sources | length' main-abc.js.map
  5. 5

    Reconstruct the tree

    A minimal script: for each `sources[i]`, normalize the path (strip `webpack://`, `ng://`, `rollup://` prefixes, decode URI, drop `?query#hash`, collapse `.` / `..`) and write `sourcesContent[i]` to that path on disk. Skip webpack synthetic modules (paths containing ` lazy `, ` sync `, `!`, `|`, `(webpack)`). This is 40-60 lines of Node.js.

    const map = require('./main-abc.js.map');
    map.sources.forEach((src, i) => {
      if (!map.sourcesContent?.[i]) return;
      if (/[!|]|\(webpack\)/.test(src)) return;
      const clean = src.replace(/^webpack:\/*/, '').split(/[?#]/)[0];
      require('fs').mkdirSync(require('path').dirname(clean), { recursive: true });
      require('fs').writeFileSync(clean, map.sourcesContent[i]);
    });
  6. 6

    Let Sourcemap Explorer do it across the whole site

    The extension handles all of the above across every bundle a page loads, deduplicates shared modules across chunks, skips webpack synthetics, reconstructs the tree correctly when the same path shows up with different content in different bundles, and exports a single `.zip` with the full project tree. No scripting required. A single-map recovery is a ten-minute manual job; a whole-site recovery across 20 bundles would take hours of scripting to get right.

Real-world example

Alternative methods

reverse-sourcemap (npm)

`npx reverse-sourcemap <file.map>` produces a tree from a single map file. Doesn't handle multiple bundles, doesn't follow headers, no deduplication across maps — you're still manually collecting `.map` URLs. Good for one-off inspection of a single file.

source-map-explorer (npm)

Analyzes bundle composition (which modules contribute what KB to the final bundle). Does not recover original files. Different tool for a different question.

The `source-map` package

The canonical JavaScript library for parsing sourcemaps (by Mozilla). Useful when you need the full VLQ `mappings` decoding for line-by-line original-position lookups. Overkill for 'recover the source tree' — you only need `sources[]` + `sourcesContent[]` for that.

Troubleshooting

The `.map` URL 404s even though the header says it exists.

Some CDNs serve maps from a different origin than the script. Check CORS headers on the script itself — a `Access-Control-Expose-Headers: sourcemap` header paired with a cross-origin map URL is common. Use the full absolute URL from the header rather than resolving against the script's directory.

The `.map` file is served but with `sourcesContent: null` entries.

Some bundlers (or build configurations) emit 'external' sourcemaps that only reference original files by URL. You can't reconstruct those without fetching each original file separately. This is rare in practice; most modern bundlers inline content by default.

Sourcemap is there but corrupted or massively truncated.

Some error trackers' proxies truncate maps to a fixed size. Try fetching directly from the site's own URL rather than through a proxy.

Everything 404s — no headers, no trailing comment.

The site doesn't ship maps publicly. They may be uploaded privately to an error tracker. No recovery path in that case.

Caveats

What to do next

Once you have the reconstructed source tree, the follow-up questions are usually: how do I find the exact version of every library (see 'find the exact version of an npm package'), which files are author code vs vendored code, and what can I actually learn from reading it. The extract-package-json guide gives you the authoritative per-library version list from any sourcemap.

FAQ

Is it legal to download sourcemaps?

You're downloading something the server chose to send to your browser. You're not bypassing access controls. The resulting code is still copyrighted by its authors — use it to learn, not to rehost.

Why do production sites ship sourcemaps?

To debug production errors. Error-tracking tools like Sentry need sourcemaps to de-minify stack traces. Teams often ship the maps publicly instead of uploading them separately to the error tracker, because the workflow is simpler and the security impact feels limited.

Can I recover TypeScript from a JavaScript sourcemap?

Yes, if the team's `sourcesContent` includes the original `.ts` / `.tsx` files. Most modern build tools (Vite, Next.js SWC, Webpack with ts-loader) include them by default. The recovered files are the actual TypeScript the team wrote, not a post-compile best guess.

What if the site uses inline sourcemaps (data URLs)?

The bundle itself embeds the map as `//# sourceMappingURL=data:application/json;base64,...`. Decode the base64 and you have the map. Less common in production (larger bundles) but still happens.

Can I automate this for many sites?

Technically yes — write a Playwright script that navigates, collects script URLs, fetches maps, reconstructs trees. In practice, that duplicates what Sourcemap Explorer already does for you, minus the UI. Automation is useful if you're building a research dataset; for one-off study, the extension is faster.

Related

Skip the manual steps.

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

Install free on Chrome