Skip to content
Puranode

Guide

Leaked Bot Token in Git? Rotate It, Then Clean Up

If a bot token or API key reached a Git commit, replace it first: reset the Discord token in the Developer Portal, send /token to BotFather for Telegram, and revoke any API key beside it. GitHub's own guide makes rotation the first step, because deleting the file, or even rewriting history, does not remove copies that already exist. After that, stop tracking the file, rewrite history if the repository is shared, and move secrets into environment variables so it cannot happen again.

Published

On this page

FAQ

Frequently asked

I pushed my Discord bot token to GitHub. What do I do first?

Reset it on the Bot page of the Discord Developer Portal, put the new token in the bot's environment and restart. Then rotate any other secret that was in the same file. Cleaning the repository comes after, because copies may already exist outside it.

Does deleting the .env file remove the token from GitHub?

No. A new commit only changes the latest version, and every earlier commit still contains the file. Rotate the token, then rewrite history with git-filter-repo if you also want it out of the repository.

Why is .env still committed after I added it to .gitignore?

Git ignores only untracked files: files already tracked by Git are not affected. Run git rm --cached .env, keep .env in .gitignore, and commit. The file stays on your disk but leaves future commits.

Do I have to rewrite history if I rotated the token?

Not for safety: a rotated token no longer works. GitHub's guide says rotating a credential may be enough. Rewrite history if the repository is public or shared and you want the old values gone from it, accepting that every clone must be refreshed.

Where should bot tokens live instead?

In environment variables set by whatever runs the bot, read with process.env in Node or os.environ in Python. Use a .env file only on your own machine, listed in .gitignore, with a committed .env.example holding the names.

How much does bot hosting cost at Puranode?

Hobby is €4.99 every 30 days for one bot on 0.5 vCPU and 512 MB of RAM, with auto-restart on crash. Standard is €9.99 for up to three bots on 1 vCPU and 1 GB. Prices include VAT.

Projects referenced

This guide names other people’s work. These are their own pages, so you get the current documentation rather than our summary of it.

  • GitHub: Removing sensitive data from a repository: Revoking or rotating the secret first, git-filter-repo with --sensitive-data-removal, the force push, where old commits survive, and asking GitHub Support to remove cached views and pull request references.
  • git-filter-repo: The history rewriting tool GitHub's guide uses, with the --invert-paths, --path and --replace-text options.
  • GitHub: About push protection: Push protection blocking recognised secrets, and push protection for users being enabled by default for public repositories.
  • gitignore: Files already tracked by Git are not affected by .gitignore, and git rm --cached to stop tracking one.
  • git rm: The --cached option, which removes a file from the index while leaving it on disk.
  • git log: Listing every commit, on all branches, that touched a path.
  • Discord developer quick start: Reset Token on the Bot page, the token being shown only once, and never checking it into version control.
  • Telegram bot features: BotFather: The /token command for a compromised token, and the warning that anyone holding a token can control the bot.
  • Node.js command-line options: --env-file, added in Node 20.6.0, and the rule that a variable already set in the environment wins over the file.
  • python-dotenv: load_dotenv not overriding existing variables by default, and the advice to add .env to .gitignore.
  • Docker build context: .dockerignore excluding files from the build context.

Keep tokens in the panel, not the repo

Bot hosting sets tokens and API keys as container environment variables. Hobby runs one bot on 512 MB for €4.99 every 30 days; Standard runs up to three on 1 GB for €9.99. VAT included.