Cal.com Has productionBrowserSourceMaps: true. So Might You.
Cal.com Has productionBrowserSourceMaps: true. So Might You.
Cal.com is one of the most polished open-source Next.js projects out there. Over 34k GitHub stars. Actively maintained. Used by real companies for real scheduling. And their next.config.js ships source maps to production.
// cal.com's next.config.js
productionBrowserSourceMaps: true
One line. That's all it takes to serve your entire original source code to anyone who opens DevTools.
What source maps actually expose
Source maps are debugging files that map your minified, bundled production JavaScript back to the original source code. When they're publicly accessible, anyone can:
- Read your entire client-side codebase — every component, every utility function, every comment you left in the code
- Understand your business logic — pricing calculations, feature flags, A/B test conditions, admin route structures
- Find vulnerabilities faster — instead of reverse-engineering minified code, attackers read your source directly
- Discover API patterns — endpoint structures, request/response shapes, authentication flows
This isn't theoretical. Open DevTools on any site serving source maps, go to the Sources tab, and you'll see the original file tree. Every .tsx file. Every hook. Every API call.
How to check your project in 10 seconds
For Next.js: Open your next.config.js (or .ts, or .mjs). Search for productionBrowserSourceMaps. If it's set to true, you're exposed. If it's absent, you're fine — the default is false.
grep -r "productionBrowserSourceMaps" next.config.*
For Vite/Webpack: Check your build config for sourcemap: true in production mode. Vite defaults to false for production builds. Webpack depends on your devtool setting.
For any framework: After deploying, open DevTools in your browser, go to the Sources tab, and look for your original file structure. If you see .tsx or .ts files with readable source code, source maps are being served.
You can also check directly:
# Pick any JS bundle from your site and append .map
curl -sS -o /dev/null -w "%{http_code}" https://yoursite.com/_next/static/chunks/main-abc123.js.map
# 200 = source maps are public. 404 = you're fine.
Why Cal.com does it (and why you probably shouldn't)
Cal.com likely has productionBrowserSourceMaps: true for debugging purposes. When you're running a large open-source project with many contributors, being able to debug production issues quickly has real value. And since their code is already open-source on GitHub, the exposure argument is weaker.
But most projects aren't open source. If you're building a SaaS, an internal tool, or anything with proprietary logic, serving source maps is handing your codebase to the public.
The Sentry pattern: hidden source maps
The right approach for most teams is hidden source maps. You generate them during the build, upload them to your error tracking service (Sentry, Datadog, Bugsnag), then delete them before deployment. Your error reports get full stack traces with original file names and line numbers. Your users see only minified code.
Sentry's Next.js SDK has a built-in option for this:
// next.config.js with @sentry/nextjs
const { withSentryConfig } = require('@sentry/nextjs');
module.exports = withSentryConfig(nextConfig, {
widenClientFileUpload: true,
hideSourceMaps: true, // Deletes .map files after uploading to Sentry
});
If you're using the Sentry webpack plugin directly:
const SentryWebpackPlugin = require('@sentry/webpack-plugin');
new SentryWebpackPlugin({
include: './build',
deleteAfterCompile: true, // removes .map files post-upload
});
This gives you the debugging benefits without the exposure.
The fix
For Next.js, the fix is one line — remove productionBrowserSourceMaps: true or explicitly set it to false:
// next.config.js
const nextConfig = {
productionBrowserSourceMaps: false, // default, but be explicit
poweredByHeader: false, // while you're here
};
For Vite:
// vite.config.ts
export default defineConfig({
build: {
sourcemap: false, // or 'hidden' if uploading to error tracking
},
});
For Webpack:
// webpack.config.js (production)
module.exports = {
devtool: false, // or 'hidden-source-map' for error tracking upload
};
How this affects audit scores
In our Security Headers & Basics audit, the security-headers.info-exposure.no-source-maps check is rated high severity (weight: 3). That means failing it costs you 3 points in the denominator without earning them in the numerator. On a 21-check audit where the Information Exposure category carries 25% of the overall weight, a single high-severity failure can drop your score by 4-6 points.
Highlight, for example, scored 56 on their Security Headers audit — a D grade. Source maps in production was one of several information exposure failures that dragged them down. The category that includes source maps (Information Exposure) also covers stack trace leaking, server version headers, and custom error pages. When you fail multiple checks in the same category, the compounding effect is significant.
Cal.com, despite being a mature project, would fail the same check. Being well-built doesn't make you immune to configuration oversights.
The one-minute takeaway
Run grep -r "productionBrowserSourceMaps" next.config.* in your project right now. If you get a hit with true, change it to false. If you need source maps for error tracking, use hidden source maps with automatic deletion after upload. There's no good reason to serve them to the public.
This is exactly the kind of thing AI coding tools never set up for you. They'll scaffold your entire app, add authentication, connect your database — and leave your source code readable to anyone with a browser.