Webpack's job is to minify your JavaScript for production: variable names become single letters, whitespace vanishes, and 50 lines of readable code compress into one line of nonsense. This is fantastic for load times and bundle size. It's catastrophic for debugging. When an error lands in your dashboard as t.u is not a function at bundle.js:1:52210, you're staring at code you've never seen, trying to fix a crash in your minified output. Webpack source maps solve this by mapping that garbage back to your original TypeScript or JavaScript source files, so every stack trace tells the real story.
This guide walks through choosing the right devtool strategy for production, generating maps safely, uploading them to your error tracker, and avoiding the common pitfalls that leave you with unreadable stack traces in production.
The webpack devtool options: choosing the right strategy
Webpack has a dozen different devtool modes, each with different trade-offs. For production error tracking, only a few matter. Here's what to know:
source-map — the safest choice for production. Generates separate .js.map files with full mappings. Your bundle doesn't embed the maps; you upload them to your error tracker's private server. Stack traces decode perfectly. Bundle size: minimal impact (maps are 10–30% of bundle size, stored separately).
hidden-source-map — generates maps but doesn't reference them in the bundle. Use this if you're uploading maps through a different channel or want to serve them from a custom endpoint. Error trackers still decode correctly if you upload the maps separately.
eval-source-map or cheap-module-source-map — development only. Never use these in production; they embed the maps inside your bundle, making it huge.
For production, use source-map or hidden-source-map:
// webpack.config.js (production)
module.exports = {
mode: 'production',
devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'eval-source-map',
entry: './src/index.ts',
output: {
filename: '[name]-[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
// ... rest of config
};
Split your webpack config into separate files for webpack.config.dev.js and webpack.config.prod.js, or use environment variables to switch devtool modes. In dev, eval-source-map is fast and rebuilds instantly. In production, use source-map and upload the maps to your error tracker.
Generating maps safely without bloating your bundle
When webpack runs with devtool: 'source-map', it generates a .map file for every chunk. Here's what that looks like:
npm run build
# Output:
# dist/
# index-abc123.js
# index-abc123.js.map
# vendor-xyz789.js
# vendor-xyz789.js.map
Each .map file is a JSON document with a compressed mapping table. The key insight: the .map file is separate from your bundle. Your deployed website doesn't reference or ship the maps. Instead, you upload them to your error tracker's private server, where they stay secure and out of reach of the browser.
This is fundamentally safer than dev mode (eval-source-map), which bakes the maps into the JavaScript itself and forces browsers to download source paths and line numbers. Production maps are private, uploaded once during deploy, and never touched by the user's browser.
Never ship maps to your CDN. If you're using Webpack's default output, the maps won't be published anyway — but if you've customized your build, make sure maps stay off your public servers:
// Good: maps in dist/ but never published to CDN
// Bad: maps published to CDN (security risk)
// In your deploy script, upload maps ONLY to your error tracker
curl -X POST https://your-lighttrace-host/api/releases/web%401.2.3/files/ \
-H "Authorization: Bearer $LIGHTTRACE_KEY" \
-F "file=@dist/index-abc123.js.map"
Setting the release tag so maps correlate with errors
Source maps alone don't work. Your error tracker needs to know which maps correspond to which errors. That's where release tags come in. Every time you deploy, tag your maps and your SDK with the same release identifier — a version string, git SHA, or timestamp.
In your Sentry SDK initialization (LightTrace uses the Sentry protocol):
// src/index.ts
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: "https://<key>@your-lighttrace-host/1",
release: "web@1.2.3", // Must match your map upload
environment: "production",
tracesSampleRate: 1.0,
});
Then in your CI/CD pipeline, upload maps with the same release tag:
# In your deploy script (GitHub Actions, GitLab CI, etc.)
npm run build
npx @lighttrace/cli upload-sourcemaps dist --project web --release "web@1.2.3"
Without matching release tags, your error tracker can't find the right maps, and you're back to seeing minified stack traces.
If you're using git-based deploys, use the git SHA as the release tag. It's deterministic, never repeats, and ties every error back to an exact commit: --release "web@$GITHUB_SHA".
Automating map uploads in CI/CD
The easiest way to avoid forgetting to upload maps is to automate it. Here's a GitHub Actions workflow that builds, uploads maps, and deploys:
name: Deploy
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run build
- name: Upload source maps
run: npx @lighttrace/cli upload-sourcemaps dist --project web --release "web@${{ github.sha }}"
env:
LIGHTTRACE_KEY: ${{ secrets.LIGHTTRACE_KEY }}
- name: Deploy to production
run: npm run deploy
The critical points:
- Build first, then upload maps. Maps are generated as part of the webpack build.
- Upload before (or alongside) deploy. If your deploy fails, maps are still available for any code that did ship.
- Use a secure secret for the API key. Store
LIGHTTRACE_KEYin your CI provider's secret manager. - Don't commit
.mapfiles. They should be generated and uploaded in CI, not checked into git.
Common gotchas with webpack source maps
Maps don't upload themselves. Generating them in webpack.config.js is only step one. If you skip the CI upload, your error tracker will see minified traces with no way to decode them. Test this: deploy, trigger an error, and verify it appears readable in your dashboard.
Release tags must match exactly. If you upload maps with --release "web@1.2.3" but your SDK reports release: "web@1.2.4", the error tracker won't find the maps. One character difference = decoding fails. Use the same tag in both places.
Source paths in maps must match your error tracker's configuration. Webpack includes full source paths in the generated maps (e.g., ../../src/index.ts). Some error trackers strip prefixes or normalize paths. If there's a mismatch, decoding fails. Check your error tracker's documentation for how to configure source path handling.
Served source maps are a security risk. Maps leak your code structure, variable names, and naming conventions. Keep them private — on your error tracker's server only, never on your CDN.
Debugging with readable source-mapped stack traces
Once maps are uploaded and everything is wired up, errors land in your dashboard with readable stack traces. Instead of:
TypeError: t.u is not a function
at Object.<anonymous> (bundle.js:1:52210)
You see:
TypeError: handler.execute is not a function
at checkout.ts:47:8
at src/index.ts:12:4
Real function names, real files, real line numbers. Now you can dive into your actual source code and find the bug. For the full debugging workflow, see how to debug production errors and how to read a stack trace.
Source-mapped stack traces combine powerfully with breadcrumbs — which show you what the user did before the crash — and with GitHub source links, which jump you straight from the dashboard to the exact line on GitHub.
Next steps for webpack and production error tracking
You're ready to wire up webpack source maps. Here's the checklist:
- Set
devtool: 'source-map'inwebpack.config.prod.js(or behind an environment check). - Set a release tag in your Sentry SDK init (e.g.,
release: "web@1.2.3"). - Add a CI step to upload maps with the same release tag (use your error tracker's CLI or curl).
- Deploy and test — trigger an error and verify it's readable in your dashboard.
For more on source maps in general, the upload process, and how to debug with them, check out those guides. If you're on React or Next.js, framework-specific guides cover the same pattern with platform-specific steps.
Start tracking errors in minutes
Set up webpack source maps, point the Sentry SDK at LightTrace, and get readable production stack traces in minutes — start free.
Source maps are the difference between a stack trace that tells you the real story and one that leaves you guessing. With webpack generating them and your error tracker decoding them, every production bug becomes a code-level investigation instead of a minified mystery.