[{"data":1,"prerenderedAt":1093},["ShallowReactive",2],{"blog-securing-env-files-best-practices":3},{"id":4,"title":5,"alt":6,"author":7,"body":8,"category":1069,"description":1070,"extension":1071,"faq":1072,"image":1080,"meta":1081,"navigation":570,"path":1082,"publishedAt":1083,"seo":1084,"stem":1085,"tags":1086,"__hash__":1092},"blog\u002Fen\u002Fsecuring-env-files-best-practices.md","Securing Your .env Files: 2026 Best Practices","env file security — developer terminal with exposed API key highlighted in red warning overlay","Alex Vibe, Senior Security Dev",{"type":9,"value":10,"toc":1050},"minimark",[11,24,30,33,38,60,63,92,95,102,104,108,113,123,133,149,153,156,160,166,170,177,181,184,188,206,210,217,219,223,230,233,248,251,254,257,260,263,358,368,370,374,504,507,509,513,524,531,591,610,616,620,626,722,742,744,748,754,764,907,917,923,929,931,967,969,973,978,993,998,1004,1009,1019,1024,1027,1032,1046],[12,13,14,15,19,20,23],"p",{},"That file named ",[16,17,18],"code",{},".env"," in your project root? It's a skeleton key. Database credentials, third-party API keys, JWT signing secrets, OAuth client secrets — all sitting in plaintext, one accidental ",[16,21,22],{},"git add ."," away from public exposure.",[12,25,26,27,29],{},"Leaked ",[16,28,18],{}," files are one of the most common (and most expensive) causes of cloud account takeover. GitHub's secret scanning catches thousands of exposed credentials daily. The ones it misses end up on dark web paste sites within hours. This guide covers what actually matters in 2026: entropy math for secrets, proper storage architecture, and the tooling to generate secrets that don't need rotating in a week.",[31,32],"hr",{},[34,35,37],"h2",{"id":36},"why-env-files-are-attacker-gold","Why .env Files Are Attacker Gold",[12,39,40,41,44,45,47,48,51,52,55,56,59],{},"A compromised database password costs you one database. A compromised AWS ",[16,42,43],{},"ACCESS_KEY_ID"," costs you the entire cloud account — compute, storage, and every service attached to that identity. Attackers know this. Automated scanners crawl GitHub, GitLab, and Bitbucket continuously, indexing new commits within minutes. Some focus specifically on ",[16,46,18],{}," extensions and common variable name patterns like ",[16,49,50],{},"DB_PASSWORD",", ",[16,53,54],{},"SECRET_KEY",", and ",[16,57,58],{},"API_TOKEN",".",[12,61,62],{},"The threat model has three layers:",[64,65,66,77,86],"ul",{},[67,68,69,73,74,76],"li",{},[70,71,72],"strong",{},"Accidental commit"," — developer runs ",[16,75,22],{}," without checking what's staged",[67,78,79,82,83,85],{},[70,80,81],{},"Exposed container"," — Docker image built with ",[16,84,18],{}," baked into a layer, pushed to a public registry",[67,87,88,91],{},[70,89,90],{},"Log injection"," — application logs full environment variable dumps on error, logs shipped to a third-party aggregator",[12,93,94],{},"All three are preventable. None of them require a sophisticated attacker. That's the problem.",[12,96,97],{},[98,99],"img",{"alt":100,"src":101},"env file security — automated scanner crawling GitHub repositories for exposed dotenv secrets","\u002Fimages\u002Fblog\u002Fsecuring-env-files-best-practices\u002Fattacker-scan.webp",[31,103],{},[34,105,107],{"id":106},"the-seven-mistakes-that-get-devops-teams-fired","The Seven Mistakes That Get DevOps Teams Fired",[109,110,112],"h3",{"id":111},"_1-missing-gitignore-entry","1. Missing .gitignore Entry",[12,114,115,116,118,119,122],{},"The obvious one, but teams still ship it. Every project should have ",[16,117,18],{}," in ",[16,120,121],{},".gitignore"," before the first commit — not after the first deployment.",[124,125,130],"pre",{"className":126,"code":128,"language":129},[127],"language-text","# .gitignore\n.env\n.env.local\n.env.*.local\n.env.production\n","text",[16,131,128],{"__ignoreMap":132},"",[12,134,135,138,139,141,142,144,145,148],{},[70,136,137],{},"Critical:"," ",[16,140,121],{}," doesn't untrack files already committed. If ",[16,143,18],{}," is in your git history, run ",[16,146,147],{},"git rm --cached .env"," and rotate every secret in it immediately. Assume it's been scraped.",[109,150,152],{"id":151},"_2-sharing-secrets-via-slack-or-email","2. Sharing Secrets via Slack or Email",[12,154,155],{},"Secrets passed through messaging apps live in search indexes, notification caches, and third-party servers forever. Use a secrets manager or at minimum a self-destructing share tool — never a DM.",[109,157,159],{"id":158},"_3-committing-envexample-with-real-values","3. Committing .env.example With Real Values",[12,161,162,165],{},[16,163,164],{},".env.example"," is supposed to contain placeholder values. Real credentials in the example file defeat the entire point. Audit the example file before every commit.",[109,167,169],{"id":168},"_4-identical-secrets-across-environments","4. Identical Secrets Across Environments",[12,171,172,173,176],{},"Reusing the same ",[16,174,175],{},"JWT_SECRET"," between development, staging, and production means a staging breach exposes production tokens. Environment-specific secrets, always — no exceptions.",[109,178,180],{"id":179},"_5-long-lived-secrets-with-no-rotation-policy","5. Long-Lived Secrets With No Rotation Policy",[12,182,183],{},"API keys issued three years ago and never rotated are the digital equivalent of a master key taped to the front door. Set a rotation schedule — 90 days maximum for production secrets.",[109,185,187],{"id":186},"_6-overly-permissive-file-permissions","6. Overly Permissive File Permissions",[12,189,190,191,193,194,197,198,201,202,205],{},"On Linux\u002FmacOS, ",[16,192,18],{}," files should be ",[16,195,196],{},"600"," (owner read\u002Fwrite only). Check with ",[16,199,200],{},"ls -la .env"," and fix with ",[16,203,204],{},"chmod 600 .env",". World-readable env files on shared servers are a silent data exfiltration vector.",[109,207,209],{"id":208},"_7-secrets-in-cicd-environment-variables-that-get-logged","7. Secrets in CI\u002FCD Environment Variables That Get Logged",[12,211,212,213,216],{},"CI platforms often echo environment variables in build logs. Always mask secrets in your CI platform's settings and avoid ",[16,214,215],{},"echo $SECRET_KEY"," in build scripts. A single unmasked log line shipped to a third-party aggregator is a breach.",[31,218],{},[34,220,222],{"id":221},"the-math-how-secure-is-your-secret-actually","The Math: How Secure Is Your Secret Actually?",[12,224,225,226,229],{},"A secret's strength is measured in ",[70,227,228],{},"entropy bits"," — the number of bits required to exhaust its search space. The formula:",[12,231,232],{},"$$H = L \\times \\log_2(R)$$",[12,234,235,236,239,240,243,244,247],{},"Where: ",[70,237,238],{},"H"," = entropy in bits, ",[70,241,242],{},"L"," = secret length in characters, ",[70,245,246],{},"R"," = character pool size (charset).",[12,249,250],{},"For a 32-character hex secret (16 possible values per character):",[12,252,253],{},"$$H = 32 \\times \\log_2(16) = 32 \\times 4 = 128 \\text{ bits}$$",[12,255,256],{},"For a 64-character alphanumeric+symbol secret (94 possible values):",[12,258,259],{},"$$H = 64 \\times \\log_2(94) \\approx 64 \\times 6.55 \\approx 420 \\text{ bits}$$",[12,261,262],{},"Here's what those entropy levels mean against real 2026 hardware — an RTX 4090 running at ~23 billion SHA-256 guesses per second:",[264,265,266,285],"table",{},[267,268,269],"thead",{},[270,271,272,276,279,282],"tr",{},[273,274,275],"th",{},"Secret Format",[273,277,278],{},"Length",[273,280,281],{},"Entropy (bits)",[273,283,284],{},"RTX 4090 Crack Time (SHA-256)",[286,287,288,303,317,331,344],"tbody",{},[270,289,290,294,297,300],{},[291,292,293],"td",{},"Hand-typed \"secret\"",[291,295,296],{},"~10 chars",[291,298,299],{},"~47 bits",[291,301,302],{},"Under 1 hour offline",[270,304,305,308,311,314],{},[291,306,307],{},"4-digit PIN",[291,309,310],{},"4 chars",[291,312,313],{},"13 bits",[291,315,316],{},"Instant",[270,318,319,322,325,328],{},[291,320,321],{},"UUID v4",[291,323,324],{},"36 chars",[291,326,327],{},"122 bits",[291,329,330],{},"Heat death of the universe",[270,332,333,336,339,342],{},[291,334,335],{},"32-char hex (CSPRNG)",[291,337,338],{},"32 chars",[291,340,341],{},"128 bits",[291,343,330],{},[270,345,346,349,352,355],{},[291,347,348],{},"64-char alphanumeric",[291,350,351],{},"64 chars",[291,353,354],{},"~420 bits",[291,356,357],{},"Physically impossible",[12,359,360,363,364,367],{},[70,361,362],{},"The hand-typed secret is the real enemy."," Most developers will write something like ",[16,365,366],{},"myappsecret2026"," and move on. That's ~47 bits of entropy — crackable in under an hour offline. Your brain is a terrible CSPRNG.",[31,369],{},[34,371,373],{"id":372},"secret-storage-architecture-what-to-use-and-when","Secret Storage Architecture: What to Use and When",[264,375,376,395],{},[267,377,378],{},[270,379,380,383,386,389,392],{},[273,381,382],{},"Storage Method",[273,384,385],{},"Local Dev",[273,387,388],{},"CI\u002FCD",[273,390,391],{},"Production",[273,393,394],{},"Notes",[286,396,397,415,431,448,462,476,490],{},[270,398,399,404,407,410,412],{},[291,400,401,403],{},[16,402,18],{}," file",[291,405,406],{},"✅ Acceptable",[291,408,409],{},"❌ Never",[291,411,409],{},[291,413,414],{},"Git-ignored only",[270,416,417,420,423,426,428],{},[291,418,419],{},"CI\u002FCD secrets UI",[291,421,422],{},"—",[291,424,425],{},"✅ Masked vars",[291,427,422],{},[291,429,430],{},"GitHub Actions, GitLab CI",[270,432,433,436,439,442,445],{},[291,434,435],{},"HashiCorp Vault",[291,437,438],{},"Optional",[291,440,441],{},"✅",[291,443,444],{},"✅ Best self-hosted",[291,446,447],{},"Dynamic secrets support",[270,449,450,453,455,457,459],{},[291,451,452],{},"AWS Secrets Manager",[291,454,422],{},[291,456,441],{},[291,458,441],{},[291,460,461],{},"IAM-integrated, auto-rotation",[270,463,464,467,469,471,473],{},[291,465,466],{},"GCP Secret Manager",[291,468,422],{},[291,470,441],{},[291,472,441],{},[291,474,475],{},"Per-access audit logs",[270,477,478,481,483,485,487],{},[291,479,480],{},"Doppler",[291,482,441],{},[291,484,441],{},[291,486,441],{},[291,488,489],{},"SaaS, team sharing, DX-friendly",[270,491,492,495,497,499,501],{},[291,493,494],{},"Hardcoded in source",[291,496,409],{},[291,498,409],{},[291,500,409],{},[291,502,503],{},"No exceptions",[12,505,506],{},"For most teams shipping on AWS, the answer is AWS Secrets Manager in production and masked CI\u002FCD variables in pipelines. Doppler is the smoothest DX if you want one unified interface across all environments.",[31,508],{},[34,510,512],{"id":511},"generating-secrets-that-are-actually-secure","Generating Secrets That Are Actually Secure",[12,514,515,516,519,520,523],{},"Rule zero: never generate secrets manually. Not with ",[16,517,518],{},"date | md5",", not with a UUID pulled from a website that uses ",[16,521,522],{},"Math.random()",", not with a memorable phrase you hash once. These approaches fail because their entropy sources are either predictable, biased, or far too small.",[12,525,526,527,530],{},"Use a ",[70,528,529],{},"CSPRNG"," — a Cryptographically Secure Pseudo-Random Number Generator. On a Unix system:",[124,532,536],{"className":533,"code":534,"language":535,"meta":132,"style":132},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# 256-bit hex key\nopenssl rand -hex 32\n\n# 512-bit base64 key (for HS512 JWT secrets)\nopenssl rand -base64 64\n","bash",[16,537,538,547,565,572,578],{"__ignoreMap":132},[539,540,543],"span",{"class":541,"line":542},"line",1,[539,544,546],{"class":545},"sHwdD","# 256-bit hex key\n",[539,548,550,554,558,561],{"class":541,"line":549},2,[539,551,553],{"class":552},"sBMFI","openssl",[539,555,557],{"class":556},"sfazB"," rand",[539,559,560],{"class":556}," -hex",[539,562,564],{"class":563},"sbssI"," 32\n",[539,566,568],{"class":541,"line":567},3,[539,569,571],{"emptyLinePlaceholder":570},true,"\n",[539,573,575],{"class":541,"line":574},4,[539,576,577],{"class":545},"# 512-bit base64 key (for HS512 JWT secrets)\n",[539,579,581,583,585,588],{"class":541,"line":580},5,[539,582,553],{"class":552},[539,584,557],{"class":556},[539,586,587],{"class":556}," -base64",[539,589,590],{"class":563}," 64\n",[12,592,593,594,601,602,605,606,609],{},"For a browser-based workflow — our ",[70,595,596],{},[597,598,600],"a",{"href":599},"\u002Fsecret-key-generator","Secret Key Generator"," — Zero-Knowledge, processing happens entirely in your browser's volatile memory, nothing is ever transmitted to a server — generates cryptographically secure keys using the ",[70,603,604],{},"Web Crypto API"," (",[16,607,608],{},"crypto.getRandomValues()","). Choose output format (hex, base64, alphanumeric) and entropy level (128, 256, or 512 bits). Done in one click.",[12,611,612,613,615],{},"Avoid tools that use ",[16,614,522],{},". It's a deterministic algorithm with a predictable seed. Tools built on it aren't just weak — they're cryptographically broken.",[109,617,619],{"id":618},"recommended-entropy-by-variable-type","Recommended Entropy by Variable Type",[12,621,622],{},[98,623],{"alt":624,"src":625},"env file security — entropy level reference guide for API keys JWT secrets and database passwords","\u002Fimages\u002Fblog\u002Fsecuring-env-files-best-practices\u002Fentropy-guide.webp",[264,627,628,641],{},[267,629,630],{},[270,631,632,635,638],{},[273,633,634],{},"Variable Type",[273,636,637],{},"Recommended Entropy",[273,639,640],{},"Output Format",[286,642,643,654,664,674,684,693,703,712],{},[270,644,645,648,651],{},[291,646,647],{},"Database password",[291,649,650],{},"256 bits",[291,652,653],{},"Alphanumeric + symbols",[270,655,656,659,661],{},[291,657,658],{},"JWT signing secret (HS256)",[291,660,650],{},[291,662,663],{},"Base64 or hex",[270,665,666,669,672],{},[291,667,668],{},"JWT signing secret (HS512)",[291,670,671],{},"512 bits",[291,673,663],{},[270,675,676,679,681],{},[291,677,678],{},"HMAC API key",[291,680,650],{},[291,682,683],{},"Hex",[270,685,686,689,691],{},[291,687,688],{},"Session cookie secret",[291,690,650],{},[291,692,683],{},[270,694,695,698,700],{},[291,696,697],{},"OAuth client secret",[291,699,650],{},[291,701,702],{},"Alphanumeric",[270,704,705,708,710],{},[291,706,707],{},"Webhook signing secret",[291,709,650],{},[291,711,683],{},[270,713,714,717,719],{},[291,715,716],{},"Encryption key (AES-256)",[291,718,650],{},[291,720,721],{},"Base64",[12,723,724,725,731,732,738,739,59],{},"For HMAC-signed webhooks and API authentication, use the ",[70,726,727],{},[597,728,730],{"href":729},"\u002Fhash-generator","Hash Generator"," to verify HMAC-SHA256 signatures client-side — same Web Crypto API approach, zero server exposure. And if you need unique session tokens or correlation IDs, the ",[70,733,734],{},[597,735,737],{"href":736},"\u002Fuuid-generator","UUID Generator"," produces 122-bit cryptographically random UUIDs via ",[16,740,741],{},"crypto.randomUUID()",[31,743],{},[34,745,747],{"id":746},"runtime-hardening-after-youve-fixed-the-file","Runtime Hardening: After You've Fixed the File",[12,749,750,751,753],{},"Getting the ",[16,752,18],{}," file right is table stakes. Runtime hardening is where teams actually prevent production incidents.",[12,755,756,759,760,763],{},[70,757,758],{},"Validate on startup."," Every required environment variable should be validated at application boot. If ",[16,761,762],{},"DATABASE_URL"," isn't present, crash immediately with a clear error — don't silently deploy a half-configured app that fails later in production.",[124,765,769],{"className":766,"code":767,"language":768,"meta":132,"style":132},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const required = ['DATABASE_URL', 'JWT_SECRET', 'API_KEY'];\nfor (const key of required) {\n  if (!process.env[key]) throw new Error(`Missing required env var: ${key}`);\n}\n","typescript",[16,770,771,820,842,902],{"__ignoreMap":132},[539,772,773,777,781,785,788,791,793,795,798,801,803,805,807,809,812,814,817],{"class":541,"line":542},[539,774,776],{"class":775},"spNyl","const",[539,778,780],{"class":779},"sTEyZ"," required ",[539,782,784],{"class":783},"sMK4o","=",[539,786,787],{"class":779}," [",[539,789,790],{"class":783},"'",[539,792,762],{"class":556},[539,794,790],{"class":783},[539,796,797],{"class":783},",",[539,799,800],{"class":783}," '",[539,802,175],{"class":556},[539,804,790],{"class":783},[539,806,797],{"class":783},[539,808,800],{"class":783},[539,810,811],{"class":556},"API_KEY",[539,813,790],{"class":783},[539,815,816],{"class":779},"]",[539,818,819],{"class":783},";\n",[539,821,822,826,828,830,833,836,839],{"class":541,"line":549},[539,823,825],{"class":824},"s7zQu","for",[539,827,605],{"class":779},[539,829,776],{"class":775},[539,831,832],{"class":779}," key ",[539,834,835],{"class":783},"of",[539,837,838],{"class":779}," required) ",[539,840,841],{"class":783},"{\n",[539,843,844,847,850,853,856,858,861,864,867,870,873,876,880,883,886,889,892,894,897,900],{"class":541,"line":567},[539,845,846],{"class":824},"  if",[539,848,605],{"class":849},"swJcz",[539,851,852],{"class":783},"!",[539,854,855],{"class":779},"process",[539,857,59],{"class":783},[539,859,860],{"class":779},"env",[539,862,863],{"class":849},"[",[539,865,866],{"class":779},"key",[539,868,869],{"class":849},"]) ",[539,871,872],{"class":824},"throw",[539,874,875],{"class":783}," new",[539,877,879],{"class":878},"s2Zo4"," Error",[539,881,882],{"class":849},"(",[539,884,885],{"class":783},"`",[539,887,888],{"class":556},"Missing required env var: ",[539,890,891],{"class":783},"${",[539,893,866],{"class":779},[539,895,896],{"class":783},"}`",[539,898,899],{"class":849},")",[539,901,819],{"class":783},[539,903,904],{"class":541,"line":574},[539,905,906],{"class":783},"}\n",[12,908,909,912,913,916],{},[70,910,911],{},"Never log environment variables."," Disable any debug middleware that dumps ",[16,914,915],{},"process.env"," in error responses. Scrub secrets from error tracking — Sentry has explicit scrubbing configuration for this.",[12,918,919,922],{},[70,920,921],{},"Use separate IAM roles per service."," Each service should only access the secrets it needs — principle of least privilege. A compromised API server shouldn't have read access to your backup encryption key.",[12,924,925,928],{},[70,926,927],{},"Set secret TTLs."," AWS Secrets Manager and Vault both support automatic rotation. Enable it. A rotated secret that gets compromised is a non-event. A static secret that gets compromised is an incident review, a postmortem, and possibly a breach notification letter.",[31,930],{},[932,933,934,939,942],"blockquote",{},[12,935,936],{},[70,937,938],{},"🛡️ Security Checkpoint — Complete This Step",[12,940,941],{},"Before your next deploy, audit every secret in your stack — weak keys and accidental commits are the leading cause of cloud account takeover in 2026.",[64,943,944,954,961],{},[67,945,946,947,950,951,953],{},"→ ",[597,948,949],{"href":599},"Generate cryptographically secure secrets"," — replace any hand-typed or weak ",[16,952,18],{}," values immediately",[67,955,946,956,960],{},[597,957,959],{"href":958},"\u002Fpassword-strength-checker","Check secret strength via entropy calculator"," — verify existing secrets meet 256-bit minimums",[67,962,946,963,966],{},[597,964,965],{"href":729},"Generate HMAC-verified API keys"," — add message authentication to webhook and API integrations",[31,968],{},[34,970,972],{"id":971},"frequently-asked-questions","Frequently Asked Questions",[12,974,975],{},[70,976,977],{},"Is it safe to commit a .env file to GitHub?",[12,979,980,981,983,984,986,987,989,990,992],{},"No. Never commit ",[16,982,18],{}," files to version control. Add ",[16,985,18],{}," to ",[16,988,121],{}," before your first commit — not after the first deployment. If you've already pushed a ",[16,991,18],{}," file, treat every secret in it as fully compromised. GitHub indexes commits permanently, even after deletion and history rewrites. Rotate everything immediately: revoke API keys at the provider level, cycle database credentials, generate new signing secrets with a CSPRNG.",[12,994,995],{},[70,996,997],{},"What should I use instead of storing secrets in .env?",[12,999,1000,1001,1003],{},"For production environments, use a dedicated secrets manager: HashiCorp Vault for self-hosted infrastructure, AWS Secrets Manager or GCP Secret Manager for cloud-native deployments, or Doppler if you want a team-friendly SaaS layer across all environments. ",[16,1002,18],{}," files are acceptable only for local development — never in CI\u002FCD pipelines or production containers. The key distinction is that secrets managers inject values at runtime and never write to disk.",[12,1005,1006],{},[70,1007,1008],{},"How long should a secret key be?",[12,1010,1011,1012,1015,1016,1018],{},"Minimum 256 bits (32 bytes) for symmetric keys and database passwords. For JWT signing secrets using HS256, use 256 bits; for HS512, use 512 bits. Never hand-type a secret — generate with ",[16,1013,1014],{},"openssl rand -hex 32"," or the ",[597,1017,600],{"href":599}," which uses the Web Crypto API directly in your browser.",[12,1020,1021],{},[70,1022,1023],{},"What happens if my .env file gets committed to a public repo?",[12,1025,1026],{},"Rotate every secret immediately — assume it's been scraped within minutes. Use the BFG Repo Cleaner to purge it from git history, then force-push the cleaned branch. Notify third-party API providers to revoke the exposed keys. Check your cloud provider's access logs for unauthorized activity from the moment of the commit forward. If any secrets had admin-level access, audit all actions taken under those credentials.",[12,1028,1029],{},[70,1030,1031],{},"Should I encrypt my .env file?",[12,1033,1034,1035,1037,1038,1041,1042,1045],{},"Encryption at rest helps against physical compromise (stolen laptop) but doesn't address the primary threats: accidental commits and container image leaks. A better model is a secrets manager that never writes to disk and injects values at runtime. If you must store an encrypted ",[16,1036,18],{},", tools like ",[16,1039,1040],{},"sops"," (paired with AWS KMS or ",[16,1043,1044],{},"age"," encryption) are the standard approach for teams that can't yet migrate to a full secrets manager.",[1047,1048,1049],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":132,"searchDepth":549,"depth":549,"links":1051},[1052,1053,1062,1063,1064,1067,1068],{"id":36,"depth":549,"text":37},{"id":106,"depth":549,"text":107,"children":1054},[1055,1056,1057,1058,1059,1060,1061],{"id":111,"depth":567,"text":112},{"id":151,"depth":567,"text":152},{"id":158,"depth":567,"text":159},{"id":168,"depth":567,"text":169},{"id":179,"depth":567,"text":180},{"id":186,"depth":567,"text":187},{"id":208,"depth":567,"text":209},{"id":221,"depth":549,"text":222},{"id":372,"depth":549,"text":373},{"id":511,"depth":549,"text":512,"children":1065},[1066],{"id":618,"depth":567,"text":619},{"id":746,"depth":549,"text":747},{"id":971,"depth":549,"text":972},"Security","Your .env file holds database passwords, API keys, and signing secrets. One git push and they're public forever. Here's the complete security checklist — for real this time.","md",[1073,1075,1077],{"question":977,"answer":1074},"No. Never commit .env files to version control. Add .env to .gitignore immediately. If you've already committed one, rotate every secret in it — GitHub indexes commits permanently, even after deletion.",{"question":997,"answer":1076},"For production use HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, or Doppler. .env files are acceptable for local development only — never in CI\u002FCD pipelines or production containers.",{"question":1078,"answer":1079},"How long should a secret key be for a .env file?","At minimum 256 bits (32 bytes) for symmetric keys. JWT and HMAC signing secrets should be 512 bits (64 bytes). Always generate with a CSPRNG — never hand-type or derive from a password.","\u002Fimages\u002Fblog\u002Fsecuring-env-files-best-practices\u002Fhero.webp",{},"\u002Fen\u002Fsecuring-env-files-best-practices","2026-05-17",{"title":5,"description":1070},"en\u002Fsecuring-env-files-best-practices",[1087,1088,1089,1090,1091],"env file security","dotenv","secret management","API keys","developer security","ockstmpxl_NKdx8S74VMzyhQHwi2EsedHkurCoUNpik",1782717489497]