You're about to send the link, and the builder says the app is published. Here's the order I'd suggest for the next twenty minutes on your own URL. Twenty minutes is my suggested pace, not a measured study of how long the work takes, and this pass isn't a certificate. It follows what a stranger can already open, then the two dashboards a public check can't see. If you only have five minutes, do the first two steps and don't skip a service role key.
The long form of the same ideas is the vibe-coded app security checklist. The notes under each step point at the longer blog pages when you want the vendor's wording.
Minutes 1 to 4: open the real URL
Paste the public address into a private window, signed out. If the page asks for the builder's login, you're looking at the editor, not the launch. Use the host a customer would use: the lovable.app link, the Bolt publish URL, the .replit.app link, or the custom domain.
View the page source. Search for these strings. You aren't exploiting anything here. You're just reading the file the browser was sent.
service_roleandsb_secret_sk-,sk-proj-,sk-ant-,sk_live,sk_test,rk_live,rk_test,whsec_BEGIN PRIVATE KEYorBEGIN RSA PRIVATE KEYpostgres://ormongodb://with an@in the host, which is what a connection string with a password looks like
Stripe says only the publishable key (pk_) is safe in front-end code. OpenAI says not to put a key in a browser. A Supabase anon key or sb_publishable_ key is supposed to be there. The anon key note shows how to read the JWT role. If any real secret is in the source, stop the pass and rotate it after you remove it. Sharing the link first and rotating later means the copy is already out.
Then open developer tools, select a script from your domain, and search for the same strings. Bundlers hide keys in JavaScript more often than in the HTML.
Minutes 5 to 8: well-known files
On the same host, open these paths in the address bar. You want a not-found page, or your normal app shell, not a list of names and values.
/.env/.env.local/.env.production/.git/HEAD/.git/config/.npmrc
A file of NAME=value lines is a public env file. A Git HEAD file that says ref: refs/heads/..., or a config file with a [core] or [remote] section, means the repository metadata is being served. How to tell a real hit from a single-page app that returns index.html for every path is covered in leaked .env, .git, and source maps. Also look in a script for sourceMappingURL. If it points at a .map file, open it. Original source in that file is published source.
Minutes 9 to 14: the database, in the dashboard
The public page can't show you policies. This is the part you do signed in, as the project owner.
If the app uses Supabase, follow Supabase's row-level security guide. For each table in an exposed schema, turn row-level security on, revoke extra grants from anon, and read every policy. using ( true ) on a private table means signed-out visitors can read it. The plain-English walk-through is here. While signed out, request one row with only the anon or publishable key. A customer row means you aren't ready.
If the app uses Firebase, open Rules for Firestore, Realtime Database, and Storage. Firebase's insecure-rules page says not to ship allow read, write: if true or root ".read": true. A rule that only checks request.auth != null on the whole database gives every signed-in user all of it. Details are in Firebase security rules mistakes. The web API key in the client is expected.
If the builder uses its own database and the docs don't describe row-level security, open that product's data settings and see whether a signed-out visitor can read private records. Bolt Database is the example in the Bolt guide: the public check can't see it. Lovable Cloud may also hide the supabase.co host. The Lovable guide says to confirm the locks in Cloud anyway.
Minutes 15 to 17: headers
Reload the site with the network panel open. Click the document. Look for Content-Security-Policy, Strict-Transport-Security (on HTTPS), X-Content-Type-Options, X-Frame-Options or frame-ancestors, and Referrer-Policy. MDN explains the frame header: if you send nothing, and you have no other restriction, the browser allows other sites to embed the page. The longer explanation is security headers for non-engineers. Missing headers are worth adding. They aren't the reason to delay a launch if a secret key is still in the bundle. Fix the key first.
What this pass deliberately skips
Don't paste a live key into a chatbot to ask whether it's real. That copies the secret to another system. Don't post the private report, or a screenshot of a real row, in a public reply. The check is for your own URL. A stranger's app is their data, even when the page is public.
This pass also doesn't try passwords, guess hidden admin paths, or sign in as a customer. Those steps are outside a look at what the browser already received. If the business takes payment or stores personal data, plan a deeper review later. Shipping the link still shouldn't wait on that review when a service role key or an open table is sitting in front of you now. Fix those first.
Minutes 18 to 20: run the check, then paste the prompt
Put the same public URL into the free check on this page. Deploy Safely fetches that page and its scripts. It reports the secret shapes it knows, including a service role JWT, and it calls out a Supabase anon JWT as expected rather than as a leak. It requests the paths in the list above. It asks Supabase for one row per table only when a supabase.co URL and a legacy anon JWT are already on the page, and it asks Firebase only when that public config is already there. It doesn't save row contents, and it doesn't use a secret it finds. It notes missing headers when two or more of the watched ones are absent. It doesn't log in, fuzz paths, or open the builder.
Read the private report. For each critical or high item, the report includes a fix prompt. Use the prompt below if you want one pass that covers the whole list. Publish again. Run the check a second time. Rotate any key that was already in the live page. The second check can confirm the new bundle. It can't pull back the old key.
If the report is quiet, you still owe yourself the dashboard steps. An empty Supabase answer can mean the table is empty. A Firebase refusal on one request can miss another path. A Lovable Cloud or Bolt Database that never appears in the page is invisible to this check. The guides index has a page per builder: Lovable, Bolt, and Replit are the three that match the way most of these apps get published. The comparison of their documented defaults stays inside what those companies wrote down.
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.
Do a launch pass on this project. Fix what a visitor can already see. Do not print secrets. 1. Remove from client code: Supabase service_role and sb_secret_ keys, OpenAI and Anthropic keys, Stripe sk_ and rk_ keys, webhook secrets, private keys, and database URLs with passwords. Keep Stripe pk_, Supabase anon or sb_publishable_, and a Firebase web API key. 2. Move provider calls to server code that reads server environment variables. Do not prefix a secret with VITE_ or NEXT_PUBLIC_. 3. Enable row-level security on every exposed Supabase table and replace policies of true on private data. If this is Firebase, replace allow-all rules. If this is a builder-hosted database, restrict private records to the signed-in user and tell me where. 4. Confirm the publish does not serve .env or .git, and turn off production source maps. 5. Add Content-Security-Policy (with frame-ancestors 'none' unless we embed on purpose), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, and Strict-Transport-Security on HTTPS. 6. List what I need to rotate. Do not disable a control to make the preview load.