A published site is a folder of files the web server is willing to hand out. Most of those files are the app. A few names should never be in that folder: .env, the .git directory, and a source map that still contains your original code. You can look for them yourself with the address bar. You don't need a scanner to do the first pass, and a scanner helps only if it knows the difference between a real file and a single-page app that answers every URL with index.html.
What an env file is, and why it ends up public
An env file is a list of names and values. Next.js loads .env files into process.env, and the default create-next-app template gitignores them. The docs say you almost never want to commit these files. Values without the NEXT_PUBLIC_ prefix stay on the server. Values with that prefix are inlined into the browser bundle at build time, which is a separate way to publish a value even when the file itself isn't on the web.
Vite loads .env, .env.local, .env.[mode], and .env.[mode].local. It says .env.local and .env.*.local are local-only and should be gitignored. It also says only variables prefixed with VITE_ are exposed in client source, and that those shouldn't hold secrets. The file becoming public is a worse case than the prefix: every name in the file is readable, including the ones Vite wouldn't have bundled.
The file reaches the host when a static export copies the project folder, when a deploy step uploads the repo root instead of the build output, or when a server is configured to serve hidden files. Lovable's own hosting isn't "your repo as a public folder," which is why this is less common on a default Lovable URL and more common after you copy an older Vite build somewhere else. Replit's secrets aren't available on Static Deployments, which is one reason a static publish is the place to look. Those limits are spelled out in the Lovable, Bolt, Replit, and v0 guides.
How to look, and what a false alarm looks like
Open https://your-domain/.env, then /.env.local and /.env.production. A real env file is plain text. Lines look like OPENAI_API_KEY= followed by a value, or DATABASE_URL= followed by a URL. You shouldn't see your app's navigation, a <!doctype html> page, or the normal "not found" layout dressed up as HTML.
Many single-page apps are configured to return index.html with status 200 for any path they don't know. That response is the app, not your secrets. If the body is HTML, you didn't find an env file, even if the URL says /.env. Treat a download of NAME=value lines as the real hit.
The same test works for /.npmrc. That file sometimes holds an _authToken for a package registry. A token there can install or publish packages as you. An npmrc that is only a registry URL, with no token, is a smaller issue. Still remove it from the public folder.
When the file is real, do these three things in this order. Remove it from the host so the URL returns a not-found page. Find the deploy setting that copied it, so the next publish doesn't put it back. Rotate every secret that was in the file. Deleting the file doesn't change the fact that it was downloaded. Rotation happens in each vendor's dashboard: OpenAI, Stripe, Supabase, the database host, and anyone else named in the file.
A public .git directory
Git stores the repository in a .git directory. The repository layout describes that directory. HEAD records what is checked out. config holds repository configuration, including remote URLs. Other files in that directory hold objects and history.
Open /.git/HEAD and /.git/config on the live host. A real HEAD is a short text file, either ref: refs/heads/ plus a branch name, or a commit hash. A real config contains a [core] or [remote] section. HTML in the body means the same false alarm as with .env.
If those two files are plain text, assume the rest of .git may be readable too. History is the real problem. A key you deleted in the latest commit can still sit in an older one. Rotating "whatever is in the latest .env" isn't enough if the Git objects are public. Rotate secrets that ever lived in the repo. Then stop the host from serving /.git. On a static host, don't copy that directory into the publish folder. On a server, deny the path. Confirm /.git/HEAD is no longer a 200 response with that short text body.
The free check doesn't download your whole history, and it doesn't try alternate paths. It requests /.git/config and /.git/HEAD only. A hit on either one is the signal to treat history as public and rotate.
Source maps
A source map is a JSON file that lets browser tools show original file names, and sometimes the original source, for a bundled script. That's useful in development. On a public production URL, it publishes the code you thought the bundler had obscured, including comments and any secret that was still in the source when the map was built.
The browser finds the map from a comment at the end of the script: sourceMappingURL= followed by a path or URL. Open a script from your domain in developer tools and search for that comment. Open the URL it names. If the JSON contains sourcesContent with your original files, those files are public.
Vite's build options say build.sourcemap defaults to false. true writes a separate .map file. 'inline' puts the map in the output file as a data URL. 'hidden' writes the map but suppresses the comment in the bundle. A check that only follows sourceMappingURL won't see a hidden map, because nothing in the script points at it. Hidden doesn't delete the file. If the .map is still deployed at a guessable path, it can be opened directly. Prefer false for a public build unless you upload maps to a private error tracker and keep them off the website.
Next.js says browser source maps are disabled in production unless you set productionBrowserSourceMaps: true. When that flag is on, Next.js emits the maps next to the JavaScript and serves them when requested. Leave the flag off for a public app. Development source maps are a different switch. They're on so you can debug locally. They aren't what this section is about, unless you published the development server by mistake.
Turning maps off for the next build doesn't remove a .map file already on the host. Delete those files. If the map contained a key, rotate the key. The map is a copy of the source, not the only copy that might have leaked, so check the bundle and the env file too.
What the free check does with these paths
Deploy Safely requests six paths on the host of the page you submit: /.env, /.env.local, /.env.production, /.git/config, /.git/HEAD, and /.npmrc. It doesn't report a hit when the body looks like HTML. For the env paths, it looks for lines that are an uppercase name, an equals sign, and a value, and it reports the names, not the values. For Git, it looks for a config section or a HEAD ref. For /.npmrc, it reports the path only when an _authToken setting is present, and it doesn't save the token.
Source maps are checked only when a script the page already loaded contains a sourceMappingURL comment that isn't a data URL. The check fetches that map. It reports it when the JSON includes original source content, and it lists a few original file names. It doesn't save the source. A hidden map with no comment, or a map that has file names but no source content, isn't reported. Search for those yourself if you turned 'hidden' on and still deployed the files.
The same run still reads the page and its scripts for secret keys, and it still notes missing security headers when two or more of the watched ones are absent. It still talks to Supabase or Firebase only when that config is already in the page. It doesn't brute-force file names, and it doesn't log in. Pair it with the checklist and, if you're about to send the link, the 20-minute launch pass.
A prompt you can paste
Paste this into the builder, publish again, and run the check a second time. If a secret was already on the live site, rotate it in that vendor's dashboard. The prompt cannot do that part.
Stop this project from publishing secret files or production source maps. 1. Make sure the deploy output does not include .env, .env.local, .env.production, .npmrc, or the .git directory. On Next.js, keep .env files gitignored. On Vite, do not put secrets in VITE_ variables. 2. Set Vite build.sourcemap to false, or Next.js productionBrowserSourceMaps to false. Delete any .map files already on the host. Do not rely on hidden maps if the .map file is still publicly reachable. 3. Add a not-found response for /.env, /.git/HEAD, and /.git/config. A rewrite that returns index.html for those paths is fine only if the body is the app, not the secret file. 4. List every secret name that was in a public env file, Git history, or source map so I can rotate the values. Do not print the values. 5. Do not disable an auth check or a database rule as part of this cleanup. After the next publish, /.env and /.git/HEAD should not return those files.