Guides / Source maps exposed in production? How to check and turn them off

last checked oct 2026

Source maps exposed in production? How to check and turn them off

A source map is a .map file that turns your minified bundle back into the code you wrote. If your build ships them publicly, anyone can open DevTools and read your original front-end source, comments included.

The short answer

For every JavaScript file your site loads, request the same URL with .map on the end. If you get back JSON that starts with {"version":3, your source maps are public. To stop it, turn off production source maps in your build (build.sourcemap in Vite, productionBrowserSourceMaps in Next.js), or keep generating them for your error tracker but delete the .map files before deploy. On Vercel you can also turn on Protected Source Maps.

A public source map isn't a vulnerability on its own. Your bundle is already public, and the map only makes it readable. The real risk is what's in that readable code: a hardcoded key, an admin route you assumed nobody could find, or a comment explaining a check that only runs in the browser. If any of that is there, attackers can now read it easily.

How to check

1. Check your live site

This lists the scripts your homepage loads and checks whether a matching .map file is served. It only prints file names and a status, never file contents:

SITE=https://your-app.com
for js in $(curl -s "$SITE/" | grep -oE '(src|href)="[^"]+\.js"' | cut -d'"' -f2 | sort -u); do
  case "$js" in http*) url="$js" ;; /*) url="$SITE$js" ;; *) url="$SITE/$js" ;; esac
  if curl -s "$url.map" | head -c 100 | grep -q '"version"'; then
    echo "EXPOSED  $url.map"
  else
    echo "ok       $url.map"
  fi
done

It checks the response body rather than the status code on purpose. Single-page apps on Vercel or Netlify are often set up to answer every unknown path with index.html and a 200, so a 200 alone doesn't prove a map exists.

Two more places a map can be announced:

# a sourceMappingURL comment at the end of a bundle (prints only the file name)
curl -s "https://your-app.com/assets/index-abc123.js" | tail -c 300 | grep -o 'sourceMappingURL=[^ ]*'

# a SourceMap response header
curl -sI "https://your-app.com/assets/index-abc123.js" | grep -i '^sourcemap'

Don't stop if both come back empty. Vite's 'hidden' mode removes the comment but still writes the .map file next to the bundle, so it gets deployed unless something deletes it. The loop above catches that case because it requests the .map URL directly.

If a map is exposed, see whether it carries your full source. This prints a count, not the code:

curl -s "https://your-app.com/assets/index-abc123.js.map" | grep -c '"sourcesContent"'

A result of 1 means the map embeds your original source files as text (the sourcesContent field), so anyone can rebuild them file by file.

The quicker way: our free Leak Checker flags public source maps as part of its passive check, along with exposed keys and missing security headers.

2. Check your project

# is source map output turned on?
grep -nE "sourcemap|productionBrowserSourceMaps|GENERATE_SOURCEMAP" \
  vite.config.* next.config.* .env* package.json 2>/dev/null

# after a production build: did any .map files land in the output folder?
npm run build
find dist build out .next/static -name "*.map" 2>/dev/null | head

Apps from Lovable and Bolt are usually Vite projects (output in dist/). v0 apps are Next.js. On Replit or Cursor projects, check which framework your package.json uses.

How to fix it

Vite (Lovable, Bolt and most React SPAs)

Vite's default is false, so production source maps usually mean someone turned them on. Remove the setting or set it back:

// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    sourcemap: false, // true, 'inline' and 'hidden' all put source in your deploy
  },
})

'inline' is the worst of the three: it appends the whole map to the JS file as a data URI, so there's no separate .map request to block.

Next.js (v0 and most Vercel apps)

Next.js doesn't generate browser source maps for production builds unless you opt in. If you find this, remove it or set it to false:

// next.config.js
module.exports = {
  productionBrowserSourceMaps: false,
}

When it's true, Next.js writes the maps next to the JS files and serves them automatically.

Create React App

Older CRA apps generate source maps for production builds unless you turn them off. Set this in .env or in your build command:

GENERATE_SOURCEMAP=false

If you use Sentry (or another error tracker)

You can keep readable stack traces without publishing your code. Generate maps, upload them during the build, then delete them so they never reach your host. With the Sentry Vite plugin:

// vite.config.ts
import { defineConfig } from 'vite'
import { sentryVitePlugin } from '@sentry/vite-plugin'

export default defineConfig({
  build: { sourcemap: 'hidden' },
  plugins: [
    // keep the Sentry plugin last
    sentryVitePlugin({
      org: 'your-org',
      project: 'your-project',
      authToken: process.env.SENTRY_AUTH_TOKEN, // set in CI, never in code
      sourcemaps: { filesToDeleteAfterUpload: ['./dist/**/*.map'] },
    }),
  ],
})

Without a plugin, the generic version is a post-build step that runs after your upload: find dist -name "*.map" -delete.

On Vercel: Protected Source Maps

Vercel can serve .map files only to people who can already access the deployment and return a 404 to everyone else. Turn it on in your project under Settings → Deployment Protection → Protected Source Maps. Vercel says it's on by default for new projects, but existing projects have to opt in. It applies on the next .map request, with no redeploy needed. It doesn't cover inline maps, so 'inline' still has to go.

Then redeploy and re-check

Run the loop from step 1 again against the live site. Every line should say ok. If the maps were public and your source contained a key that isn't meant to be public, treat that key as leaked. Deleting the maps doesn't make it secret again, and the key is usually still in the bundle itself. Follow our guide to rotating exposed API keys.

next step

Check your live app for free

Our free Leak Checker looks at your deployed app the way any visitor's browser does and flags exposed secret keys, public source maps and missing security headers. Paste your URL to see whether your source maps are public.

If you'd rather have the whole app looked at, the $99 Scan + Report covers your repo and live app, including Supabase RLS, leaked keys and auth checks.

Sources

Every product step above was checked against these official pages. Last checked Oct 2026. Products change; if a menu moved, the docs below are the source of truth.