<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[VEILVM Blog]]></title><description><![CDATA[VEILVM Blog]]></description><link>https://veilvm.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:21:26 GMT</lastBuildDate><atom:link href="https://veilvm.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Source Maps in Production: How They Leak Your Code]]></title><description><![CDATA[You minified your bundle, maybe even obfuscated it, and shipped. Then someone opens DevTools and sees your original files: folder structure, variable…]]></description><link>https://veilvm.hashnode.dev/source-maps-in-production-how-they-leak-your-code</link><guid isPermaLink="true">https://veilvm.hashnode.dev/source-maps-in-production-how-they-leak-your-code</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Security]]></category><category><![CDATA[webdev]]></category><category><![CDATA[frontend]]></category><dc:creator><![CDATA[VEILVM]]></dc:creator><pubDate>Thu, 05 Nov 2026 07:57:59 GMT</pubDate><content:encoded><![CDATA[<p>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 <strong>public source map</strong>. 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.</p>
<h2>What a source map contains</h2>
<p>A source map is a JSON file (usually <code>app.js.map</code>) 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.</p>
<p>The important field is <code>sourcesContent</code>:</p>
<pre><code class="language-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,..."
}
</code></pre>
<p>If <code>sourcesContent</code> is present, the map holds a <strong>full copy of your original source</strong>, including comments. Even without it, <code>sources</code> and <code>names</code> reveal your file layout and original identifiers.</p>
<h2>How the leak happens</h2>
<p>The built file ends with a comment like:</p>
<pre><code class="language-js">//# sourceMappingURL=app.min.js.map
</code></pre>
<p>If that <code>.map</code> 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.</p>
<p>Common causes:</p>
<ul>
<li><code>devtool: "source-map"</code> left on in a webpack production config</li>
<li><code>build.sourcemap: true</code> in Vite, copied from a tutorial</li>
<li>framework defaults that changed between versions</li>
<li>CI uploading the whole <code>dist/</code> folder, maps included</li>
<li>a CDN or error-monitoring setup that needed maps and published them publicly</li>
</ul>
<p>Source maps also <strong>undo obfuscation</strong>. If you obfuscate and then generate a map that points back to the original, you have shipped the key to your own lock.</p>
<h2>How to check your site</h2>
<ol>
<li>Open your site, then DevTools → Sources. If you see a <code>src/</code> or <code>webpack://</code> tree with readable files, maps are loading.</li>
<li>Look at the last line of each production JS file for <code>sourceMappingURL</code>.</li>
<li>Request the map directly:</li>
</ol>
<pre><code class="language-bash">curl -sI https://example.com/assets/index-abc123.js.map
# HTTP 200 means it is public
</code></pre>
<ol start="4">
<li>Check for the <code>SourceMap</code> or <code>X-SourceMap</code> response header on JS files, which also points to maps.</li>
<li>Search old deployments too. Hashed files from previous builds often stay on CDNs for months.</li>
</ol>
<h2>Fixing it by tool</h2>
<h3>Vite</h3>
<pre><code class="language-js">// vite.config.js
export default {
  build: {
    sourcemap: false        // no maps at all
    // or "hidden": generate maps but omit the sourceMappingURL comment
  }
};
</code></pre>
<p><code>"hidden"</code> is useful if you upload maps to an error tracker. You still need to make sure the <code>.map</code> files are not deployed publicly.</p>
<h3>webpack</h3>
<pre><code class="language-js">module.exports = {
  mode: "production",
  devtool: false               // or "hidden-source-map"
};
</code></pre>
<p>Avoid <code>eval-*</code> and <code>inline-*</code> variants in production: inline maps embed the full source inside the JS file itself.</p>
<h3>Next.js</h3>
<p><code>productionBrowserSourceMaps</code> 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.</p>
<h3>esbuild and Rollup</h3>
<p>esbuild: omit <code>--sourcemap</code>, or use <code>--sourcemap=external</code> and keep the files out of the deploy. Rollup: set <code>output.sourcemap</code> to <code>false</code> or <code>"hidden"</code>.</p>
<h2>Keep debugging without leaking</h2>
<p>You can have readable production stack traces without publishing source:</p>
<ul>
<li><strong>Hidden maps + private upload.</strong> Generate hidden maps, upload them to Sentry or a similar service during CI, then delete them before deploy.</li>
<li><strong>Exclude sources content.</strong> Some tools allow maps without <code>sourcesContent</code>. This still reveals names and paths, so prefer private storage.</li>
<li><strong>Restrict access.</strong> Serve <code>.map</code> files only from an internal host or behind authentication if your team needs them in the browser.</li>
</ul>
<p>A simple CI guard catches regressions:</p>
<pre><code class="language-bash">if find dist -name "*.map" | grep -q .; then
  echo "Source maps found in dist/, refusing to deploy"; exit 1
fi
</code></pre>
<h2>Already leaked? What to do</h2>
<ol>
<li>Remove the maps from your origin and purge the CDN cache, including old hashed files.</li>
<li>Assume the source was copied. Review it for secrets, internal URLs and admin endpoints.</li>
<li><strong>Rotate any credential</strong> that appeared in the source, even in a comment.</li>
<li>Check that server-side authorization does not rely on obscurity of endpoint names.</li>
<li>If valuable logic was exposed, consider moving it to the server or protecting it in future builds.</li>
</ol>
<h2>Source maps and code protection</h2>
<p>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.</p>
<h2>FAQ</h2>
<h3>Should I disable source maps in production?</h3>
<p>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.</p>
<h3>Can someone recover my code from a source map?</h3>
<p>Yes. If the map includes <code>sourcesContent</code>, it contains your original files verbatim, and free tools can rebuild the whole project tree from a site URL.</p>
<h3>Do source maps affect performance or SEO?</h3>
<p>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.</p>
<h2>Protect what is left</h2>
<p>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 <a href="https://vm.ldvmp.com/?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=source-maps-leak-source-code">https://vm.ldvmp.com</a>.</p>
]]></content:encoded></item></channel></rss>