Production Source Maps: Debugging Tool or Accidental Public Artifact?
Source maps are not automatically a security flaw. But accidentally public maps can reveal source code, filenames, and project structure. Audit build, deployment, and error monitoring deliberately.
Your production site serves a minified JavaScript bundle. Next to it, app.js.map is reachable. Developer tools suddenly show component names, file paths, and functions that were much harder to recognise in the bundled output.
Is that automatically a security vulnerability? No.
Is it something the team should have decided deliberately? Absolutely.
Source maps are debugging artifacts. They connect generated, bundled, or minified code back to original source files. ECMA-426 standardises the format specifically for source-level debugging and server-side stack-trace deobfuscation. That makes source maps genuinely valuable in production: an error at chunk-8f31c.js:1:18452 can be mapped back to a useful line in a React, TypeScript, or other source file.
The risk is not simply that maps exist. It is that nobody knows whether they are intended to be public, what they contain, and whether the deployment pipeline enforces that decision.
Myth: "Source maps reveal our secret code"
Client-side JavaScript is not confidential by design. The browser has to download it in order to execute it. Minification makes analysis less convenient; it does not provide access control.
Source maps can, however, make that analysis dramatically easier. Depending on how they are generated, a map may contain:
- original filenames and paths,
- module and directory structure,
- mappings from generated positions to original positions,
- function or variable names,
- and optionally the original source itself in
sourcesContent.
When sourcesContent is present, the browser does not need to retrieve the original files separately. The map can carry the source text directly.
That does not make every public source map an incident. It can still expose information a team did not intend to publish as a development artifact: comments, internal filenames, feature names, legacy paths, or the organisation of non-public modules.
One boundary remains non-negotiable: a real secret must never depend on client-side obscurity. If an API token, password, or other sensitive credential was injected into browser code or a source map during the build, deleting the .map file is not enough. Revoke or rotate the credential and fix the client/server boundary.
Three production models — all can be valid
There is no universal source-map configuration for every website. The correct choice depends on the purpose.
1. Fully public source maps
This can be intentional, for example for an open-source application or a product where browser-level debugging is deliberately supported. The important part is that the decision is documented.
Next.js provides a useful concrete example. Browser source maps are disabled by default for production builds. If productionBrowserSourceMaps is enabled, Next.js outputs the maps alongside the JavaScript files and serves them when requested. That setting therefore means more than "better stack traces": it changes what the production site exposes.
2. Source maps only for error monitoring
For many commercial sites, this is the most useful model. The production build generates accurate maps, CI/CD uploads them to an error-tracking service, and the web server does not publish them to visitors.
webpack describes hidden-source-map as a possible production mode for this use case. The map is generated, but the bundle does not include a reference comment. webpack also warns that when maps exist only for error reporting, they should not be deployed to the web server.
The word "hidden" deserves care. It means the generated bundle does not point to the map. If your deployment job still copies every .map file to a public bucket, the file remains publicly reachable.
Sentry documents the same operational pattern: generate maps, upload them during the build, then either prevent web access to .js.map files or remove the maps after upload using the build-plugin configuration.
3. Maps without embedded source code
webpack also provides nosources-source-map, which omits sourcesContent. This can preserve stack-trace mapping without directly embedding all original source code in the map.
It is not an invisibility mode. webpack explicitly notes that filenames and structure can still be exposed.
So the useful decision is not simply "maps or no maps". It is: who needs which debugging information, and where is that information allowed to live?
The second blind spot: the map belongs to the wrong release
A source map is useful only when it matches the exact generated artifact being executed.
Imagine this sequence:
- Release A produces
app.abc123.jsand its matching map. - Release B changes the code.
- The CDN already serves bundle B, but the error tracker only has maps from A.
- An error is deobfuscated and points to a source line that is no longer correct.
That can be worse than an unreadable stack trace because the result looks plausible and sends developers in the wrong direction.
A reliable release process therefore binds maps to the artifact that was actually deployed. Modern error-monitoring systems may use release metadata, debug IDs, or similar mechanisms. Operationally, the important rule is simpler: map upload and deployment belong to the same reproducible release pipeline.
Manually uploading maps at some later point is not a dependable release process.
Seven red flags in a source-map audit
1. Nobody knows whether production generates maps
The setting lives somewhere in framework, bundler, or plugin configuration and has not been reviewed since the last relaunch.
2. .map files automatically enter the public artifact
CI copies the whole build directory to a CDN, object store, or web server without a deliberate include/exclude rule.
3. hidden-source-map is mistaken for "not public"
The reference comment is absent, but the file can still be requested at a predictable path.
4. Nobody inspects sourcesContent
The team checks only whether a map exists, not which source files and contents it embeds.
5. Error monitoring receives maps from a different build
Local, CI, and production builds use different flags, environment values, or minimisers. The uploaded map does not correspond to the shipped bundle.
6. Old maps remain on the CDN forever
A new release stops publishing maps, but historical hashed assets remain reachable. A deliberate policy also needs a retention decision for old releases.
7. A credential leak is "fixed" by deleting the map
If a real secret reached a public build artifact, removal is not enough. Once a credential was publicly retrievable, assume it may have been copied and rotate it.
A practical 30-minute audit
Step 1: Read the build configuration
Search for framework and bundler options such as:
productionBrowserSourceMaps,devtool,sourcemap,hidden-source-map,nosources-source-map,- source-map plugins from your error-monitoring provider.
Do not record only the current value. Record the intended policy: public, monitoring-only, or disabled.
Step 2: Inspect the real production artifact
After a production build, answer:
- Which
.mapfiles were generated? - Which bundles contain
sourceMappingURL? - Do the maps contain
sourcesContent? - Which filenames and paths appear under
sources? - Are browser and server maps handled separately?
Inspect the artifact that will actually be deployed, not just a local development directory.
Step 3: Test from outside
Open a production JavaScript asset and check whether it contains a source-map reference. Then deliberately request the corresponding .map file.
If your policy is "private", the map should not simply return publicly with 200 OK.
Do not rely only on the absence of the comment. A known or predictable asset path may remain reachable without a reference.
Step 4: Verify error monitoring
Trigger a known JavaScript error in a controlled production or preview environment. Confirm that the stack trace resolves to the expected source file and line.
That single test checks several things at once:
- maps were uploaded,
- they match the release,
- release or debug metadata is correct,
- and the monitoring system does not merely contain "some source maps".
Step 5: Review old releases
CDNs and object stores often retain hashed assets for a long time by design. That is useful for caching. If your policy now says source maps should not be public, decide what happens to historical maps and whether they still serve an operational purpose.
Make source maps a CI rule, not a guess
The durable fix is not a one-time cleanup of .map files. Make the policy testable.
A release check can:
- fail on unexpected
.mapfiles in the public artifact, - inspect
sourceMappingURLin production bundles, - verify the upload to error monitoring before deployment,
- block the release when maps would become public against policy,
- treat browser and server artifacts separately,
- scan build output for sensitive token patterns,
- and run a smoke test against the deployed asset URL.
That turns "I think Next.js does not serve those" into a reproducible property of the release process.
What Website-Pflichtencheck would inspect
A technical source-map review can examine:
- JavaScript and CSS artifacts actually served in production,
- public
.mapURLs andsourceMappingURLreferences, sources,sourcesContent, and visible project structure,- Next.js, webpack, Vite, or other bundler configuration,
- differences between development, preview, and production,
- source-map uploads to error-monitoring systems,
- matching maps to the correct release,
- CDN and object-storage retention of old maps,
- build-artifact scanning and CI controls,
- and the response to accidentally shipped credentials.
The goal is not to ban source maps. Good production diagnostics are valuable. The goal is to treat debugging artifacts as deliberately as logs, build outputs, and deployments.
If your source maps are public, that should be a decision — not a side effect of somebody enabling a build flag two years ago.