# Source Maps in Production: How They Leak Your Code

You minified your bundle, maybe even obfuscated it, and shipped. Then someone opens DevTools and sees your original files: folder structure, variable names, comments, TODOs and all. The culprit is almost always a **public source map**. This guide explains how the leak works, how to check whether you are affected, and how to keep the debugging benefits without publishing your code.

## What a source map contains

A source map is a JSON file (usually `app.js.map`) that maps positions in your built output back to your original files. Browsers load it when DevTools is open, so stack traces and breakpoints show real code.

The important field is `sourcesContent`:

```json
{
  "version": 3,
  "file": "app.min.js",
  "sources": ["../src/billing/license.ts", "../src/api/client.ts"],
  "sourcesContent": ["// TODO: remove hardcoded test key\nexport function checkLicense(...", "..."],
  "mappings": "AAAA,SAASA,..."
}
```

If `sourcesContent` is present, the map holds a **full copy of your original source**, including comments. Even without it, `sources` and `names` reveal your file layout and original identifiers.

## How the leak happens

The built file ends with a comment like:

```js
//# sourceMappingURL=app.min.js.map
```

If that `.map` file is deployed to the same public directory, anyone can download it. Tools exist that take a site URL, fetch every map and rebuild the original project tree in seconds. No reverse engineering skill is needed.

Common causes:

- `devtool: "source-map"` left on in a webpack production config
- `build.sourcemap: true` in Vite, copied from a tutorial
- framework defaults that changed between versions
- CI uploading the whole `dist/` folder, maps included
- a CDN or error-monitoring setup that needed maps and published them publicly

Source maps also **undo obfuscation**. If you obfuscate and then generate a map that points back to the original, you have shipped the key to your own lock.

## How to check your site

1. Open your site, then DevTools → Sources. If you see a `src/` or `webpack://` tree with readable files, maps are loading.
2. Look at the last line of each production JS file for `sourceMappingURL`.
3. Request the map directly:

```bash
curl -sI https://example.com/assets/index-abc123.js.map
# HTTP 200 means it is public
```

4. Check for the `SourceMap` or `X-SourceMap` response header on JS files, which also points to maps.
5. Search old deployments too. Hashed files from previous builds often stay on CDNs for months.

## Fixing it by tool

### Vite

```js
// vite.config.js
export default {
  build: {
    sourcemap: false        // no maps at all
    // or "hidden": generate maps but omit the sourceMappingURL comment
  }
};
```

`"hidden"` is useful if you upload maps to an error tracker. You still need to make sure the `.map` files are not deployed publicly.

### webpack

```js
module.exports = {
  mode: "production",
  devtool: false               // or "hidden-source-map"
};
```

Avoid `eval-*` and `inline-*` variants in production: inline maps embed the full source inside the JS file itself.

### Next.js

`productionBrowserSourceMaps` is off by default. If you turned it on, turn it off, or upload maps to your monitoring tool and delete them from the output before deploying.

### esbuild and Rollup

esbuild: omit `--sourcemap`, or use `--sourcemap=external` and keep the files out of the deploy. Rollup: set `output.sourcemap` to `false` or `"hidden"`.

## Keep debugging without leaking

You can have readable production stack traces without publishing source:

- **Hidden maps + private upload.** Generate hidden maps, upload them to Sentry or a similar service during CI, then delete them before deploy.
- **Exclude sources content.** Some tools allow maps without `sourcesContent`. This still reveals names and paths, so prefer private storage.
- **Restrict access.** Serve `.map` files only from an internal host or behind authentication if your team needs them in the browser.

A simple CI guard catches regressions:

```bash
if find dist -name "*.map" | grep -q .; then
  echo "Source maps found in dist/, refusing to deploy"; exit 1
fi
```

## Already leaked? What to do

1. Remove the maps from your origin and purge the CDN cache, including old hashed files.
2. Assume the source was copied. Review it for secrets, internal URLs and admin endpoints.
3. **Rotate any credential** that appeared in the source, even in a comment.
4. Check that server-side authorization does not rely on obscurity of endpoint names.
5. If valuable logic was exposed, consider moving it to the server or protecting it in future builds.

## Source maps and code protection

Removing maps is the first step of any JavaScript protection plan; there is no point obfuscating code you also publish in its original form. After that, minification removes comments and names, standard obfuscation hides structure, and VM-based protection compiles critical functions into custom bytecode so they cannot be mapped back to readable JS. That raises the cost of reverse engineering a lot, though nothing that runs on a user's device is unbreakable. See how to protect JavaScript source code for the full layered approach and how attackers deobfuscate JavaScript for what you are defending against.

## FAQ

### Should I disable source maps in production?

Disable public source maps, yes. You can still generate hidden maps and upload them privately to an error-monitoring service so stack traces stay readable for your team.

### Can someone recover my code from a source map?

Yes. If the map includes `sourcesContent`, it contains your original files verbatim, and free tools can rebuild the whole project tree from a site URL.

### Do source maps affect performance or SEO?

Browsers only download maps when DevTools is open, so normal users are not slowed down. They have no SEO benefit. The cost is purely the information they expose.

## Protect what is left

Once maps are gone, the shipped code is still readable after formatting. If you want to see how VM protection treats a sensitive function, VEILVM has a browser trial with no sign-up: 3 runs a day, files up to 5 KB (resets at 00:00 UTC). Standard requests are processed in memory and your code is never written to logs. Try it at [https://vm.ldvmp.com](https://vm.ldvmp.com/?utm_source=hashnode&utm_medium=post&utm_campaign=source-maps-leak-source-code).
