What is a source map? How browsers find them and when to ship them

TL;DR

A source map is a JSON file that links minified production JavaScript back to the original source files. For any line and column in the built bundle, it records which source file, line, column and name it came from. Developer tools and error trackers use it to turn an unreadable stack trace back into your own code.

In this guide

What's inside a source map?

Here's a three-line function built with esbuild, minified, with source maps turned on.

JavaScript
export function formatTotal(order) {  return order.totals.grand.toFixed(2);}

The build writes two files. The bundle has the code squeezed onto one line, with a comment at the end:

JavaScript
function o(t){return t.totals.grand.toFixed(2)}export{o as formatTotal};//# sourceMappingURL=cart.js.map

And cart.js.map next to it holds this:

JSON
{  "version": 3,  "sources": ["../src/cart.js"],  "sourcesContent": ["export function formatTotal(order) {\n  return order.totals.grand.toFixed(2);\n}\n"],  "mappings": "AAAO,SAASA,EAAYC,EAAO,CACjC,OAAOA,EAAM,OAAO,MAAM,QAAQ,CAAC,CACrC",  "names": ["formatTotal", "order"]}

sources lists the original files. names holds the identifiers the minifier renamed, which is how o becomes formatTotal again. mappings is the actual map: each comma-separated chunk is a run of Base64 numbers saying "this column in the output came from that file, line, column and name". You'll never read it by hand.

sourcesContent is the one to notice. It's optional, and it embeds your original source code, comments included, inside the map. Whether you publish that field is most of the decision later on this page.

How does a browser find the source map?

A browser or tool finds a source map in one of three ways: a sourceMappingURL comment at the end of the JavaScript file, a SourceMap HTTP header on the response that served the file, or a map inlined as a base64 data URL in that same comment. A relative URL resolves against the JavaScript file's own address.

MethodWhat it looks likeGood for
Comment//# sourceMappingURL=cart.js.map on the last lineThe default in almost every bundler
HTTP headerSourceMap: /assets/cart.js.mapAdding maps without rebuilding the files
Inline//# sourceMappingURL=data:application/json;base64,eyJ2…Local development only

CSS uses the same idea with a /*# sourceMappingURL=… */ comment. The older X-SourceMap header still works in most tools, but SourceMap is the current name.

Inline maps belong in development. They put the whole map inside the bundle, so every visitor downloads it, and a map is usually bigger than the code it describes. A separate .map file costs your visitors nothing, because browsers only fetch it when someone opens the developer tools.

Should you ship source maps to production?

For most web apps, yes. Your JavaScript is already public, because every visitor downloads it, and minification shrinks files rather than protecting anything. A public source map gives away your original names, file layout and comments. It doesn't give away anything a determined person couldn't already work out from the bundle, and it makes every production error readable.

That's my view, and there are honest reasons to disagree. If the comments say things you wouldn't put in a public repo, if the client-side code is genuinely what customers pay for, or if file names announce features you haven't launched, publishing sourcesContent hands all of that over neatly formatted. Note what isn't on that list: secrets. An API key in a source map was already in the bundle, and the map isn't the problem.

ApproachWho can read your sourceReadable traces in developer toolsWorks with tools that fetch maps
Public mapsAnyone who looksYes, for everyoneYes
Hidden maps, uploaded to an error trackerOnly that trackerNoNo
No mapsNobodyNoNo

Hidden maps are the usual middle ground: the bundler writes the .map files but leaves out the comment, and you upload the maps to your error tracker and keep them off the public site. That works if one tool is all you need. It also means anything else that reads maps from your site, Rebase included, finds nothing, and your own developer tools show minified code in production unless you load a map by hand.

There's a half step too. A map without sourcesContent still turns o back into formatTotal at src/cart.js:2, but doesn't publish the code itself. You can open the file locally.

How to turn source maps on

Every major build tool has a setting for public maps, and most have a hidden variant.

ToolPublic mapsHidden maps
Vitebuild.sourcemap: truebuild.sourcemap: 'hidden'
webpackdevtool: 'source-map'devtool: 'hidden-source-map'
Rollupoutput.sourcemap: trueoutput.sourcemap: 'hidden'
esbuild--sourcemap--sourcemap=external
Next.jsproductionBrowserSourceMaps: trueNo built-in option

How to check your source maps work

Open your site, find the main bundle in the Network panel, and check its last line for the comment. Then request the map yourself:

Shell
curl -sI https://shop.example.com/assets/index-B7c2x9Qe.js.map

You want a 200 and a JSON content type. The usual failures are boring. The deploy step skipped .map files, a CDN rule blocked them, or the map on the server came from a different build than the bundle it sits next to, so every line points somewhere slightly wrong. That last one is the nastiest, because nothing errors. Build the bundle and its map in the same step, and deploy them together.

For support teams

If engineering says an error report is "minified and useless", it usually means the source maps aren't reachable from the place that's reading them. That's a build setting, not a missing piece of the report, and it's worth raising once rather than living with.

Found this useful? Pass it on to your team.