A branch deploy is a public copy of your app, often with prod secrets
The concrete Netlify branch deploy security risk is this: every branch deploy and deploy preview gets its own URL, that URL is reachable by anyone who has it, and by default it runs with the same environment variables your production site uses. So a feature branch or a split-testing variant can serve the exact same secrets as prod, from a URL you never meant to publish.
The URLs are predictable. A branch deploy lives at a name like branch-name--your-site.netlify.app, and a deploy preview lives at deploy-preview-123--your-site.netlify.app. These are not gated behind a login. Netlify also does not add a noindex rule for them automatically, so a stray link in a PR, a Slack message, or a crawler can surface them.
Two things are worth separating here. What is observable from outside is the URL itself and whatever the deployed bundle ships: client-side env vars, source maps, response headers, and any build artifact left in the published directory. What is only possible, not proven, is that a given branch holds a real secret. A branch deploy having access to a server-side variable does not mean that value reaches the browser. The point of a pre-launch check is to find out which of these is true for your specific branch, not to assume the worst.
- A branch deploy URL is public and unauthenticated unless you add protection.
- It inherits the production environment by default, so its server functions can read the same keys prod reads.
- Client-exposed variables (anything prefixed for the browser) ship in the branch bundle exactly as they would in prod.
Why AI-built apps hit this on Netlify
An AI-built app usually reaches Netlify through a fast path: connect the Git repo, accept the detected build command, set a few environment variables once, and deploy. That single set of environment variables applies to all deploy contexts unless you scope them, and "scope your variables per context" is not a step the generator or the happy-path tutorial walks you through.
Split testing makes the surface wider. Netlify split testing serves different branches to a slice of real visitors from your production domain, so a branch that was only ever meant for an experiment is now live traffic. If that branch was built quickly and never reviewed for what it exposes, the experiment ships the exposure too.
- The default for Netlify environment variable scope is "all contexts," so a key added for production is also present in branch and preview builds.
- AI-generated apps tend to put more in the client than needed, because a single fetch that works in the browser is the fastest thing to generate.
- A Netlify deploy preview is unauthenticated by default, and nobody on a fast-launch path stops to lock it down.
- The build that runs is the one in the branch, not the polished main branch, so a half-finished commit can expose more than your reviewed code does.
Check what your branch deploy actually serves
You can confirm all of this on your own site without attacking anything. Open the branch deploy URL or deploy preview URL directly, then check four things from the public surface.
Client env vars: open DevTools, then the Sources or Network tab, and search the loaded JavaScript for your variable prefixes (for example VITE_, NEXT_PUBLIC_, PUBLIC_) and for secret-shaped tokens such as sk_, pk_live, sk-ant-, or service_role. A publishable or anon key appearing here is expected and fine. A secret-shaped string that should be server-only is the suspicious result.
Source maps: in the Network tab, look for .map files loading next to your bundles, or a sourceMappingURL comment at the end of a JS file. If maps are present, your original source is readable from the branch URL. That is not a secret by itself, but it makes any embedded value easier to find.
Build artifacts: request paths that should never be published, such as GET /.env, /.git/HEAD, or a stray /config.json. A 404 is the safe result. A 200 that returns file contents is a finding, and a branch build is more likely to have left one behind than a reviewed prod build.
Headers and indexing: check the response headers of the branch URL for the same security headers you expect in prod, and check whether the page allows indexing. A branch deploy missing X-Frame-Options or Content-Security-Policy, or one that search engines can index, is reachable in ways you probably did not intend.
- Safe result: only publishable/anon keys in the bundle, no .map files in prod-style branches, 404 on .env and .git, headers present, indexing blocked.
- Suspicious result: a server-only key in the client, readable source maps, a 200 on a build artifact, or an indexable preview carrying real data.
- Repeat per branch. Each branch deploy is its own build, so a clean main branch tells you nothing about a feature branch.
Scope variables and protect the URL, and know the gaps
Two fixes do most of the work, and they address different problems.
First, scope your environment variables by deploy context. In Netlify, set production-only secrets to the production context rather than all contexts, so branch and preview builds never receive them. Keep anything the browser genuinely needs as a publishable value only, and move every true secret behind server-side functions that read context-scoped variables. This is the core of fixing Netlify split testing env vars leaking into experiments.
Second, restrict who can reach the URL. Netlify password protection or role-based access on deploy previews and branch deploys turns an open URL into a gated one. That is an authentication control: it decides who gets in. It does not fix what the build exposes once they are in, so it is a complement to scoping variables, not a replacement.
What these fixes do not cover: scoping variables and gating URLs is an exposure and access control, not an authorization review. It does not check whether your app lets a logged-in user read another user's data, and it does not validate your business logic. Those need code and manual review. This maps to the OWASP category of security misconfiguration and the CWE class of exposure of sensitive information, used here as taxonomy, not as proof your branch is vulnerable.
- Move secrets to the production context only; verify branch builds no longer carry them.
- Disable source map publishing for any branch that serves real traffic, including split-testing branches.
- Add a noindex header or rule to branch and preview deploys so they do not get crawled.
- Rotate any secret that was reachable from a public branch URL, because you cannot prove it was never read.
Where VibeCodeGuard fits, and what it does not prove
VibeCodeGuard's Launch Check scans a branch deploy URL the same way it scans a production URL, from the outside. Point it at the branch or preview address before you promote it, and it reports the public-surface signals: security headers, source maps left in the build, exposed client-side environment variables, secret-shaped strings in the bundle, and exposed build or deploy artifacts, each with severity and fix direction. The value of running it against the branch URL specifically is that you see the experiment's exposure before split testing puts it in front of real visitors.
What it does not do is read your Netlify dashboard or your variable scoping. It works from the public surface, so it cannot confirm that a server-side key is correctly context-scoped; it can only tell you whether that key reached the browser. It is not a penetration test, a code review, or an authorization check, and a clean scan does not make the branch safe to promote on its own.
Next step: list the branch and preview URLs that are currently live or in a split test, open each one, and run a focused Launch Check against it before promoting. If a branch carries a server-only secret, find it in your scan rather than in someone else's traffic.
A clean Launch Check on a branch URL proves only that the public surface looks clean from outside. It does not read your env-var scoping, your access rules, or your authorization logic, and it does not confirm a secret was never read while the branch was reachable. Treat a flagged branch as a promotion blocker and a clean one as a single signal.
> launch check
Scan the public surface before launch.
Get severity, evidence, and practical fix guidance for the checks VibeCodeGuard can run from the outside.