I committed my .env file to GitHub. What now?

Checked on

Open the file on GitHub and check what is in it. Any real secret in it (a Stripe secret key, a Supabase secret or service_role key, an OpenAI key, a database password) counts as leaked, even if the repository is private. Create a new key in that service, put the new key where your app reads it, and turn off the old key. Then stop Git from tracking the file. Deleting the file in a new commit doesn't remove it from the history. In a Lovable project, don't delete .env: it only holds values that are meant to be public.

Who sees this

You keep your app's code on GitHub, often through your AI tool's GitHub sync (Lovable, Bolt, Replit, Cursor or v0). A .env file with your keys went into a commit. Sometimes the AI tool did it for you. Then GitHub sends a warning email, Stripe or OpenAI emails you about an exposed key, or you spot the file yourself.

The common questions are "the repo is private, does it still count as leaked?" and "I deleted the file, so is it gone?". The answers are below.

First, check what is in the file

Not every value in a .env file is a secret. Open the file on GitHub and sort each line into one of two groups.

Meant to be public (no action needed):

  • Values that start with VITE_ or NEXT_PUBLIC_. Your build copies these into the code every visitor downloads, so they are public anyway.
  • Your Supabase project URL and the anon key or publishable key (sb_publishable_...). Supabase says the publishable key can be public, including in source code. What decides who can read and change your data is Row Level Security, not this key. See the Supabase anon key in the browser

Real secrets (rotate them):

  • Stripe secret keys (sk_live_..., sk_test_...) and restricted keys (rk_...), and webhook signing secrets (whsec_...)
  • A Supabase secret key (sb_secret_...) or the legacy service_role key
  • AI keys (OpenAI sk-..., Anthropic and others), email keys (Resend and others)
  • Database connection strings with a password, SMTP passwords, anything named SECRET, PRIVATE or PASSWORD

If the file only has public values, you don't need to rotate anything.

If you built with Lovable

Lovable puts a .env file in your project on purpose. It holds VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY. Lovable's docs say these can be public, and that you should not add .env to .gitignore in a Lovable project, because the preview and published builds need it. If you delete it, the preview breaks. Real secrets in Lovable go in Secrets, not in .env, and Edge Functions read them from there.

So in a Lovable project: keep .env, check that it only has VITE_ values, and move anything else to Secrets (then rotate it, see below).

Does a private repository change anything?

Not much. Treat a real secret in a private repository as leaked too. Stripe's advice is to rotate a key that was exposed "even if you aren't sure anyone saw it". OpenAI's advice says keys in a private repository can still leak in a data breach. Anyone you invited, any app you connected to the repository, and every copy someone downloaded can see the file. Rotating a key takes a few minutes and ends the question.

If the repository is public, act now. GitHub scans public repositories for known key formats and tells some providers, which may turn the key off for you. Don't wait for that: providers say they can't promise to catch every leak.

How to fix it, step by step

1. Rotate every real secret, starting with the most dangerous

Do this first. GitHub's own docs say that once a secret is rotated it can no longer be used, and that this may be all you need. Do the Stripe secret key and the Supabase secret key first, because they give full access to your payments and your data.

  • Stripe: open the API keys page in the Stripe Dashboard. Rotate the secret key, or create a new one and then expire the old one. For a webhook signing secret, open that webhook endpoint in the Dashboard and roll its signing secret.
  • Supabase: Settings → API Keys. Create a new secret key, put it everywhere your app uses the old one, then delete the old secret key. If the leaked key was the legacy service_role key, deactivate the legacy keys, and also revoke the legacy JWT secret, as Supabase's guide "Rotate a leaked or compromised API key" explains. Your browser code then needs the new publishable key in place of the old anon key.
  • OpenAI and other AI tools: open the API keys page, create a new key, then delete the old one. Set a monthly spending limit so a leaked key can't run up a large bill.
  • Database passwords: change the password in your database provider and update the connection string.

2. Put the new keys in the right place

The new keys must not go back into a file in your repository.

  • Lovable: Cloud or Supabase Secrets for backend keys (Stripe, Resend, OpenAI).
  • Supabase Edge Functions: Edge Functions → Secrets in the dashboard, or supabase secrets set NAME=value.
  • Vercel, Netlify, Replit, Railway: the project's environment variables or Secrets page. Redeploy afterwards so the app reads the new values.

Check that the app still works: sign up, log in and open the payment page.

3. Stop Git tracking the file

Adding .env to .gitignore alone doesn't work if Git already tracks the file. Git's own docs say you have to remove it from the index first:

git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Stop tracking .env"
git push

This keeps the file on your computer and removes it from the next commits. Add a .env.example file with the variable names and no values, so others know which ones to set.

Skip this step in a Lovable project if the file only has VITE_ values (see above).

4. Decide whether to clean the history

The old commits still contain the file. Once every real key in it is rotated, the old values don't work, so many people stop here. GitHub's docs agree this can be enough.

If the file also held things you can't rotate (personal data, a customer list), or you want it gone anyway, GitHub explains how to rewrite the history with git-filter-repo (version 2.47 or later, with --sensitive-data-removal). Know the limits before you start:

  • Anyone who already cloned or forked the repository keeps a copy.
  • Old pull requests lose their diffs, and everyone working on the repository has to re-clone or rebase.
  • GitHub still shows cached views of old commits until you contact GitHub Support and they remove them.

5. Stop it happening again

  • Keep .env in .gitignore from the first commit (except in Lovable, where .env holds only public VITE_ values).
  • Don't use git add . without looking. Check what you are about to commit with git status or git diff --cached.
  • GitHub's push protection blocks known key formats when you push to a public repository. It is on by default for your account. Leave it on, and don't bypass the warning.
  • Tell your AI tool where secrets go (see the prompt below).

Fix prompt for your AI builder

Paste this into Lovable, Bolt, Cursor, Replit or whichever tool you use. Rotate the keys yourself first. Never paste a real key into the chat.

My .env file was committed to GitHub. I have already rotated these keys myself: [list the key names, not the values].
1. Make sure no secret key (Stripe secret key, Supabase secret or service_role key, OpenAI or other AI keys, email keys, database passwords) is in any file in the repository or in code that runs in the browser.
2. Read those secrets only on the server: from Edge Function secrets (Deno.env.get) or the host's environment variables.
3. Keep only values that are meant to be public in .env, such as VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY. [If this is not a Lovable project: add .env to .gitignore and create .env.example with the variable names and no values.]
4. Don't change how the app works, and don't change any other file.
Tell me which secrets I need to add in the Secrets or environment variables page, by name only.

How to confirm it's fixed

  • The old keys no longer work: the provider's dashboard shows them as deleted or expired.
  • The app works with the new keys: sign-up, log-in and the payment page all open and work.
  • On GitHub, the latest version of your repository has no .env with real secrets (in Lovable, .env has only VITE_ values).
  • Your live site doesn't serve the file: opening https://your-site.com/.env in a browser shows a "not found" page, not your keys.

How to find out sooner next time

A key can leak in two other ways that GitHub won't warn you about: your live site can serve the .env file itself, or your AI tool can put a secret key into the code every visitor downloads. Heedza's security checks look at your live site from outside. They try to open /.env, /.env.local, /.git/HEAD and /.git/config, and read your home page and its code files for secret keys. If they find something, you get the finding and a fix prompt. Heedza doesn't read your GitHub repository, so it can't see a file that is only in your git history.

More on keys: OpenAI API key exposed in your frontend, Supabase service_role key exposed in the frontend, and the security checklist for apps made with AI tools.

Let Heedza check this for you. Heedza’s security checks looks for secret keys left in your page code and private files anyone can open. It checks again every time it checks your pages. It never logs in or fills in forms, so it can’t see errors that only show after that.

Made with Lovable, Bolt, Replit, Cursor or v0? Free for 1 site: start free or see prices.

More questions

I deleted the file and pushed again. Is it gone?

No. The file is still in the earlier commits, and anyone who can see the repository can open them. Rotate the keys.

GitHub blocked my push because of a secret. Did it leak?

If the push was blocked, the commit didn't reach GitHub. Remove the secret from the commit on your computer and push again. If the key was ever shared somewhere else, rotate it anyway.

My Supabase anon key is in the commit. Do I need to rotate it?

No. It's meant to be public, and it's already in the code your visitors download. Check that Row Level Security is turned on for your tables instead.

I got an email from Stripe or OpenAI about an exposed key. What does it mean?

Their systems found the key in a public place, often through GitHub's secret scanning. Some providers turn the key off for you. Follow step 1 anyway, and check your account for activity you don't recognise.

Should I just delete the repository and make a new one?

That doesn't help on its own. Copies and forks may exist, and the keys still work. Rotating the keys is what makes the leaked keys stop working.

Where this comes from