[{"data":1,"prerenderedAt":333},["ShallowReactive",2],{"blog-how-to/remove-secrets-git-history":3},{"id":4,"title":5,"body":6,"category":313,"date":314,"dateModified":315,"description":316,"draft":317,"extension":318,"faq":319,"featured":317,"headerVariant":320,"image":319,"keywords":319,"meta":321,"navigation":322,"ogDescription":323,"ogTitle":319,"path":324,"readTime":319,"schemaOrg":325,"schemaType":326,"seo":327,"sitemap":328,"stem":329,"tags":330,"twitterCard":331,"__hash__":332},"blog/blog/how-to/remove-secrets-git-history.md","How to Remove Secrets from Git History",{"type":7,"value":8,"toc":296},"minimark",[9,13,17,21,27,36,41,44,64,77,90,103,116,120,123,129,136,142,146,149,155,163,167,202,206,209,227,231,237,259,277],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-remove-secrets-from-git-history",[18,19,20],"p",{},"Cleaning up after accidental commits",[22,23,24],"tldr",{},[18,25,26],{},"TL;DR:\nFirst, rotate the compromised credential. Then use BFG Repo Cleaner to remove the secret from all commits. Force push the cleaned history. Alert team members to re-clone. Remember: if the secret was ever pushed to a public repo, consider it compromised regardless of cleanup.",[28,29,30,33],"warning-box",{},[18,31,32],{},"Critical First Step: Rotate the Credential",[18,34,35],{},"Always rotate (replace) the exposed secret BEFORE cleaning git history. Once a secret is pushed, especially to a public repo, assume it's been scraped. Cleaning history prevents future exposure but doesn't undo potential current compromise.",[37,38,40],"h2",{"id":39},"method-1-bfg-repo-cleaner-recommended","Method 1: BFG Repo Cleaner (Recommended)",[18,42,43],{},"BFG is faster and simpler than git filter-branch. It's designed specifically for removing unwanted data.",[45,46,48,53],"step",{"number":47},"1",[49,50,52],"h3",{"id":51},"install-bfg","Install BFG",[54,55,60],"pre",{"className":56,"code":58,"language":59},[57],"language-text","# macOS\nbrew install bfg\n\n# Or download the JAR file\n# https://rtyley.github.io/bfg-repo-cleaner/\n","text",[61,62,58],"code",{"__ignoreMap":63},"",[45,65,67,71],{"number":66},"2",[49,68,70],{"id":69},"clone-a-fresh-copy","Clone a fresh copy",[54,72,75],{"className":73,"code":74,"language":59},[57],"# Clone with --mirror for full history\ngit clone --mirror git@github.com:you/your-repo.git\n",[61,76,74],{"__ignoreMap":63},[45,78,80,84],{"number":79},"3",[49,81,83],{"id":82},"create-a-file-with-secrets-to-remove","Create a file with secrets to remove",[54,85,88],{"className":86,"code":87,"language":59},[57],"# secrets.txt - one secret per line\nsk_live_abc123xxxxx\nAKIA1234567890xxxxx\nghp_xxxxxxxxxxxx\n",[61,89,87],{"__ignoreMap":63},[45,91,93,97],{"number":92},"4",[49,94,96],{"id":95},"run-bfg-to-remove-secrets","Run BFG to remove secrets",[54,98,101],{"className":99,"code":100,"language":59},[57],"# Remove specific strings from all history\nbfg --replace-text secrets.txt your-repo.git\n\n# Or remove entire files\nbfg --delete-files .env your-repo.git\nbfg --delete-files \"*.pem\" your-repo.git\n",[61,102,100],{"__ignoreMap":63},[45,104,106,110],{"number":105},"5",[49,107,109],{"id":108},"clean-up-and-push","Clean up and push",[54,111,114],{"className":112,"code":113,"language":59},[57],"cd your-repo.git\n\n# Clean up the repo\ngit reflog expire --expire=now --all\ngit gc --prune=now --aggressive\n\n# Force push\ngit push --force\n",[61,115,113],{"__ignoreMap":63},[37,117,119],{"id":118},"method-2-git-filter-repo","Method 2: git filter-repo",[18,121,122],{},"A newer, Python-based alternative to filter-branch:",[54,124,127],{"className":125,"code":126,"language":59},[57],"# Install\npip install git-filter-repo\n\n# Remove a file from all history\ngit filter-repo --invert-paths --path .env\n\n# Replace text in all files\ngit filter-repo --replace-text expressions.txt\n",[61,128,126],{"__ignoreMap":63},[18,130,131,132,135],{},"Create ",[61,133,134],{},"expressions.txt",":",[54,137,140],{"className":138,"code":139,"language":59},[57],"# Format: literal:old==>new or regex:pattern==>replacement\nliteral:sk_live_abc123==>***REMOVED***\nregex:sk_live_[a-zA-Z0-9]+==>[REDACTED]\n",[61,141,139],{"__ignoreMap":63},[37,143,145],{"id":144},"method-3-git-filter-branch-legacy","Method 3: git filter-branch (Legacy)",[18,147,148],{},"The traditional method, slower but works without additional tools:",[54,150,153],{"className":151,"code":152,"language":59},[57],"# Remove a specific file from all commits\ngit filter-branch --force --index-filter \\\n  \"git rm --cached --ignore-unmatch .env\" \\\n  --prune-empty --tag-name-filter cat -- --all\n\n# Clean up\ngit reflog expire --expire=now --all\ngit gc --prune=now --aggressive\n\n# Force push all branches\ngit push origin --force --all\ngit push origin --force --tags\n",[61,154,152],{"__ignoreMap":63},[28,156,157,160],{},[18,158,159],{},"Warning: Force Push Required",[18,161,162],{},"Rewriting history requires force pushing. This will cause issues for anyone who has cloned the repo. All team members will need to re-clone or carefully rebase their local copies.",[37,164,166],{"id":165},"after-cleaning-team-coordination","After Cleaning: Team Coordination",[168,169,170,178,184,190,196],"ol",{},[171,172,173,177],"li",{},[174,175,176],"strong",{},"Notify your team"," that history was rewritten",[171,179,180,183],{},[174,181,182],{},"Ask everyone to delete and re-clone"," the repository",[171,185,186,189],{},[174,187,188],{},"Delete any forks"," that might still contain the secret",[171,191,192,195],{},[174,193,194],{},"Check CI/CD caches"," for cached copies",[171,197,198,201],{},[174,199,200],{},"Request GitHub to clear caches"," if it was a public repo",[49,203,205],{"id":204},"for-github-contact-support","For GitHub: Contact Support",[18,207,208],{},"If secrets were in a public repo, contact GitHub Support to clear their caches:",[168,210,211,221,224],{},[171,212,213,214],{},"Go to ",[215,216,220],"a",{"href":217,"rel":218},"https://support.github.com/contact",[219],"nofollow","GitHub Support",[171,222,223],{},"Select \"Sensitive data removal\"",[171,225,226],{},"Provide the repository and commit details",[37,228,230],{"id":229},"verify-secrets-are-removed","Verify Secrets Are Removed",[54,232,235],{"className":233,"code":234,"language":59},[57],"# Search all history for the secret\ngit log -p --all -S \"sk_live_abc123\" --source\n\n# If no results, the secret is removed from all commits\n\n# Also check for the file\ngit log --all --full-history -- .env\n",[61,236,234],{"__ignoreMap":63},[238,239,240,247,253],"faq-section",{},[241,242,244],"faq-item",{"question":243},"Do I really need to clean history if the repo is private?",[18,245,246],{},"It's still recommended. Private repos can become public, team members change, and the secret sits in history indefinitely. However, rotating the credential is the priority. History cleaning can be secondary for private repos.",[241,248,250],{"question":249},"Will this affect open pull requests?",[18,251,252],{},"Yes, open PRs will have conflicts after history rewrite. They'll need to be rebased or closed and reopened against the new history.",[241,254,256],{"question":255},"Can someone still see the secret from before I cleaned it?",[18,257,258],{},"If anyone cloned the repo before cleanup, they have the secret in their local history. GitHub and other hosts may also have cached copies. This is why rotating the credential is essential.",[18,260,261,264,268,269,268,273],{},[174,262,263],{},"Related guides:",[215,265,267],{"href":266},"/blog/how-to/rotate-api-keys","How to Rotate API Keys"," ·\n",[215,270,272],{"href":271},"/blog/how-to/gitignore-secrets","How to Gitignore Secrets",[215,274,276],{"href":275},"/blog/how-to/secret-scanning","How to Enable Secret Scanning",[278,279,280,286,291],"related-articles",{},[281,282],"related-card",{"description":283,"href":284,"title":285},"Step-by-step guide to preventing XSS in React and Next.js. Sanitizing user input, Content Security Policy, and common XS","/blog/how-to/protect-against-xss","How to Protect Against XSS Attacks",[281,287],{"description":288,"href":289,"title":290},"Step-by-step guide to protecting routes and API endpoints. Implement middleware patterns, authentication guards, authori","/blog/how-to/protect-routes","How to Protect Routes and API Endpoints",[281,292],{"description":293,"href":294,"title":295},"Complete guide to configuring environment variables in Railway. Set up secrets, use variable references, and manage conf","/blog/how-to/railway-env-vars","How to Set Up Railway Environment Variables",{"title":63,"searchDepth":297,"depth":297,"links":298},2,[299,307,308,309,312],{"id":39,"depth":297,"text":40,"children":300},[301,303,304,305,306],{"id":51,"depth":302,"text":52},3,{"id":69,"depth":302,"text":70},{"id":82,"depth":302,"text":83},{"id":95,"depth":302,"text":96},{"id":108,"depth":302,"text":109},{"id":118,"depth":297,"text":119},{"id":144,"depth":297,"text":145},{"id":165,"depth":297,"text":166,"children":310},[311],{"id":204,"depth":302,"text":205},{"id":229,"depth":297,"text":230},"how-to","2026-01-22","2026-02-02","Clean secrets from your git history after accidental commits. Learn to use BFG Repo Cleaner and git filter-branch to remove exposed API keys from repository history.",false,"md",null,"yellow",{},true,"Clean secrets from your git history after accidental commits.","/blog/how-to/remove-secrets-git-history","[object Object]","HowTo",{"title":5,"description":316},{"loc":324},"blog/how-to/remove-secrets-git-history",[],"summary_large_image","n9MwQyTa8_FYa3k1cbya78O57zlE6LKNNeXizI6ZTC4",1775843927829]