[{"data":1,"prerenderedAt":562},["ShallowReactive",2],{"blog-best-practices/environment-variables":3},{"id":4,"title":5,"body":6,"category":537,"date":538,"dateModified":538,"description":539,"draft":540,"extension":541,"faq":542,"featured":540,"headerVariant":547,"image":548,"keywords":548,"meta":549,"navigation":550,"ogDescription":551,"ogTitle":548,"path":552,"readTime":553,"schemaOrg":554,"schemaType":555,"seo":556,"sitemap":557,"stem":558,"tags":559,"twitterCard":560,"__hash__":561},"blog/blog/best-practices/environment-variables.md","Environment Variable Best Practices: Secrets, Configuration, and Security",{"type":7,"value":8,"toc":519},"minimark",[9,20,29,34,37,52,61,65,68,77,81,84,93,97,100,167,175,184,188,193,202,206,215,219,222,276,280,283,288,307,311,314,323,327,399,433,461,465,468,488,507],[10,11,12],"tldr",{},[13,14,15,19],"p",{},[16,17,18],"strong",{},"The #1 environment variables best practice is never committing .env files to version control."," Use .env.example for documentation, validate required variables at startup, and understand which variables are exposed to the client. These 7 practices take about 33 minutes to implement and prevent 73% of secret exposure incidents.",[21,22,23],"quotable-box",{},[24,25,26],"blockquote",{},[13,27,28],{},"\"Environment variables are the front door to your application. Leave the key under the mat, and everyone walks in.\"",[30,31,33],"h2",{"id":32},"the-golden-rule-never-commit-secrets","The Golden Rule: Never Commit Secrets",[13,35,36],{},"Your .env file should never be in version control:",[38,39,41],"code-block",{"label":40},".gitignore (essential entries)",[42,43,48],"pre",{"className":44,"code":46,"language":47},[45],"language-text","# Environment variables\n.env\n.env.local\n.env.*.local\n.env.development\n.env.production\n\n# Also ignore\n*.pem\n*.key\nconfig/secrets.json\nfirebase-adminsdk*.json\n","text",[49,50,46],"code",{"__ignoreMap":51},"",[53,54,55],"warning-box",{},[13,56,57,60],{},[16,58,59],{},"Already committed secrets?"," If you have ever committed secrets, assume they are compromised. Rotate all exposed credentials immediately, even if you removed them from the repo.",[30,62,64],{"id":63},"best-practice-1-use-envexample-for-documentation-2-min","Best Practice 1: Use .env.example for Documentation 2 min",[13,66,67],{},"Create a template file that documents required variables:",[38,69,71],{"label":70},".env.example (commit this file)",[42,72,75],{"className":73,"code":74,"language":47},[45],"# Database\nDATABASE_URL=postgresql://user:password@localhost:5432/myapp\n\n# Authentication\nJWT_SECRET=generate-a-long-random-string-here\nSESSION_SECRET=another-long-random-string\n\n# Third-party services\nSTRIPE_SECRET_KEY=sk_test_xxx\nSTRIPE_WEBHOOK_SECRET=whsec_xxx\n\n# Email\nSENDGRID_API_KEY=SG.xxx\n\n# Public (safe for client-side)\nNEXT_PUBLIC_APP_URL=http://localhost:3000\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx\n",[49,76,74],{"__ignoreMap":51},[30,78,80],{"id":79},"best-practice-2-validate-at-startup-5-min","Best Practice 2: Validate at Startup 5 min",[13,82,83],{},"Fail fast if required variables are missing:",[38,85,87],{"label":86},"Environment validation",[42,88,91],{"className":89,"code":90,"language":47},[45],"import { z } from 'zod';\n\nconst envSchema = z.object({\n  // Required\n  DATABASE_URL: z.string().url(),\n  JWT_SECRET: z.string().min(32),\n\n  // Optional with defaults\n  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n  PORT: z.coerce.number().default(3000),\n\n  // Conditional: required in production\n  STRIPE_SECRET_KEY: z.string().optional().refine(\n    (val) => process.env.NODE_ENV !== 'production' || val,\n    'STRIPE_SECRET_KEY is required in production'\n  ),\n});\n\nfunction validateEnv() {\n  const result = envSchema.safeParse(process.env);\n\n  if (!result.success) {\n    console.error('Environment validation failed:');\n    result.error.issues.forEach(issue => {\n      console.error(`  ${issue.path.join('.')}: ${issue.message}`);\n    });\n    process.exit(1);\n  }\n\n  return result.data;\n}\n\nexport const env = validateEnv();\n",[49,92,90],{"__ignoreMap":51},[30,94,96],{"id":95},"best-practice-3-understand-client-exposure-3-min","Best Practice 3: Understand Client Exposure 3 min",[13,98,99],{},"Different frameworks expose variables differently:",[101,102,103,119],"table",{},[104,105,106],"thead",{},[107,108,109,113,116],"tr",{},[110,111,112],"th",{},"Framework",[110,114,115],{},"Client-Exposed Prefix",[110,117,118],{},"Example",[120,121,122,134,145,156],"tbody",{},[107,123,124,128,131],{},[125,126,127],"td",{},"Next.js",[125,129,130],{},"NEXT_PUBLIC_",[125,132,133],{},"NEXT_PUBLIC_API_URL",[107,135,136,139,142],{},[125,137,138],{},"Vite",[125,140,141],{},"VITE_",[125,143,144],{},"VITE_APP_TITLE",[107,146,147,150,153],{},[125,148,149],{},"Create React App",[125,151,152],{},"REACT_APP_",[125,154,155],{},"REACT_APP_API_URL",[107,157,158,161,164],{},[125,159,160],{},"Nuxt",[125,162,163],{},"NUXT_PUBLIC_",[125,165,166],{},"NUXT_PUBLIC_API_URL",[53,168,169],{},[13,170,171,174],{},[16,172,173],{},"Client-exposed variables are public."," Any variable with these prefixes is bundled into your JavaScript and visible to all users. Never use them for secrets.",[38,176,178],{"label":177},"What to expose (and what not to)",[42,179,182],{"className":180,"code":181,"language":47},[45],"# SAFE for client-side (with public prefix)\nNEXT_PUBLIC_API_URL=https://api.example.com\nNEXT_PUBLIC_APP_NAME=MyApp\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx\n\n# NEVER expose (no public prefix)\nDATABASE_URL=postgresql://...\nSTRIPE_SECRET_KEY=sk_live_xxx\nJWT_SECRET=xxx\nOPENAI_API_KEY=sk-xxx\n",[49,183,181],{"__ignoreMap":51},[30,185,187],{"id":186},"best-practice-4-platform-specific-configuration-5-min","Best Practice 4: Platform-Specific Configuration 5 min",[189,190,192],"h3",{"id":191},"vercel","Vercel",[38,194,196],{"label":195},"Vercel environment variable setup",[42,197,200],{"className":198,"code":199,"language":47},[45],"# Set via Vercel CLI\nvercel env add DATABASE_URL production\n\n# Or via dashboard:\n# Project Settings > Environment Variables\n# Select environments: Production, Preview, Development\n\n# Environment-specific values\nDATABASE_URL (Production) = postgresql://prod-db...\nDATABASE_URL (Preview) = postgresql://staging-db...\n",[49,201,199],{"__ignoreMap":51},[189,203,205],{"id":204},"netlify","Netlify",[38,207,209],{"label":208},"Netlify environment configuration",[42,210,213],{"className":211,"code":212,"language":47},[45],"# In netlify.toml for non-sensitive values\n[context.production.environment]\n  API_URL = \"https://api.example.com\"\n\n[context.deploy-preview.environment]\n  API_URL = \"https://staging-api.example.com\"\n\n# Sensitive values: Site Settings > Environment Variables\n# Never put secrets in netlify.toml\n",[49,214,212],{"__ignoreMap":51},[30,216,218],{"id":217},"best-practice-5-use-secrets-managers-for-production-10-min","Best Practice 5: Use Secrets Managers for Production 10 min",[13,220,221],{},"For production, consider dedicated secrets management:",[101,223,224,234],{},[104,225,226],{},[107,227,228,231],{},[110,229,230],{},"Solution",[110,232,233],{},"Best For",[120,235,236,244,252,260,268],{},[107,237,238,241],{},[125,239,240],{},"Doppler",[125,242,243],{},"Universal secrets management",[107,245,246,249],{},[125,247,248],{},"AWS Secrets Manager",[125,250,251],{},"AWS infrastructure",[107,253,254,257],{},[125,255,256],{},"Google Secret Manager",[125,258,259],{},"GCP infrastructure",[107,261,262,265],{},[125,263,264],{},"Vault (HashiCorp)",[125,266,267],{},"Enterprise, self-hosted",[107,269,270,273],{},[125,271,272],{},"Infisical",[125,274,275],{},"Open source alternative",[30,277,279],{"id":278},"best-practice-6-rotate-secrets-regularly-5-min","Best Practice 6: Rotate Secrets Regularly 5 min",[13,281,282],{},"Have a process for rotating credentials:",[284,285,287],"h4",{"id":286},"secret-rotation-checklist","Secret Rotation Checklist:",[289,290,291,295,298,301,304],"ul",{},[292,293,294],"li",{},"Database passwords: rotate quarterly",[292,296,297],{},"API keys: rotate if team members leave",[292,299,300],{},"JWT secrets: rotate annually or on suspected breach",[292,302,303],{},"Webhook secrets: rotate if exposed",[292,305,306],{},"OAuth credentials: rotate if compromised",[30,308,310],{"id":309},"best-practice-7-different-secrets-per-environment-3-min","Best Practice 7: Different Secrets Per Environment 3 min",[13,312,313],{},"Never share secrets between environments:",[38,315,317],{"label":316},"Environment separation",[42,318,321],{"className":319,"code":320,"language":47},[45],"# Development (.env.development)\nDATABASE_URL=postgresql://localhost/myapp_dev\nSTRIPE_SECRET_KEY=sk_test_dev_xxx\n\n# Staging (.env.staging)\nDATABASE_URL=postgresql://staging-host/myapp_staging\nSTRIPE_SECRET_KEY=sk_test_staging_xxx\n\n# Production (set in platform, never in files)\nDATABASE_URL=postgresql://prod-host/myapp_prod\nSTRIPE_SECRET_KEY=sk_live_xxx\n",[49,322,320],{"__ignoreMap":51},[30,324,326],{"id":325},"common-environment-variable-mistakes","Common Environment Variable Mistakes",[101,328,329,342],{},[104,330,331],{},[107,332,333,336,339],{},[110,334,335],{},"Mistake",[110,337,338],{},"Impact",[110,340,341],{},"Prevention",[120,343,344,355,366,377,388],{},[107,345,346,349,352],{},[125,347,348],{},"Committing .env files",[125,350,351],{},"Secret exposure",[125,353,354],{},"Add to .gitignore immediately",[107,356,357,360,363],{},[125,358,359],{},"Secrets in NEXT_PUBLIC_",[125,361,362],{},"Public exposure",[125,364,365],{},"Only public config in public vars",[107,367,368,371,374],{},[125,369,370],{},"Same secrets across envs",[125,372,373],{},"Cross-environment risk",[125,375,376],{},"Unique secrets per environment",[107,378,379,382,385],{},[125,380,381],{},"No startup validation",[125,383,384],{},"Runtime failures",[125,386,387],{},"Validate required vars at boot",[107,389,390,393,396],{},[125,391,392],{},"Hardcoded fallbacks",[125,394,395],{},"Accidental prod use",[125,397,398],{},"Fail if env var missing",[400,401,402],"info-box",{},[13,403,404,407,408,415,416,415,421,426,427,432],{},[16,405,406],{},"Official Resources:"," For the latest information, see ",[409,410,414],"a",{"href":411,"rel":412},"https://12factor.net/config",[413],"nofollow","The Twelve-Factor App: Config",", ",[409,417,420],{"href":418,"rel":419},"https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions",[413],"GitHub Actions Secrets",[409,422,425],{"href":423,"rel":424},"https://vercel.com/docs/projects/environment-variables",[413],"Vercel Environment Variables",", and ",[409,428,431],{"href":429,"rel":430},"https://docs.doppler.com/",[413],"Doppler Secrets Management",".",[434,435,436,443,449,455],"faq-section",{},[437,438,440],"faq-item",{"question":439},"I committed secrets to git. What do I do?",[13,441,442],{},"Immediately rotate all exposed credentials. Remove the file from git (git rm --cached), add to .gitignore, and force push if you have already pushed. Consider the secrets compromised regardless.",[437,444,446],{"question":445},"How do I share env vars with my team?",[13,447,448],{},"Use a secrets manager (Doppler, 1Password), secure shared storage, or your platform's team features. Never share via Slack, email, or other insecure channels. Use .env.example for documentation.",[437,450,452],{"question":451},"Can I use .env in production?",[13,453,454],{},"For simple deployments, yes, but it is better to use your platform's environment variable features (Vercel, Netlify, Heroku) or a secrets manager. These provide encryption, access control, and audit logs.",[437,456,458],{"question":457},"Should I encrypt my .env.local file?",[13,459,460],{},"For local development, standard file permissions are usually sufficient. For shared environments or extra security, use tools like git-crypt or SOPS. In production, use a secrets manager instead.",[30,462,464],{"id":463},"further-reading","Further Reading",[13,466,467],{},"Put these practices into action with our step-by-step guides.",[289,469,470,476,482],{},[292,471,472],{},[409,473,475],{"href":474},"/blog/how-to/add-security-headers","Add security headers to your app",[292,477,478],{},[409,479,481],{"href":480},"/blog/checklists/pre-deployment-security-checklist","Pre-deployment security checklist",[292,483,484],{},[409,485,487],{"href":486},"/blog/getting-started/first-scan","Run your first security scan",[489,490,491,497,502],"related-articles",{},[492,493],"related-card",{"description":494,"href":495,"title":496},"Advanced secrets handling","/blog/best-practices/secrets","Secret Management",[492,498],{"description":499,"href":500,"title":501},"Vercel environment setup","/blog/best-practices/vercel","Vercel Best Practices",[492,503],{"description":504,"href":505,"title":506},"Recovery from exposure","/blog/prompts/fix-exposed-api-keys","Fix Exposed API Keys",[508,509,512,516],"cta-box",{"href":510,"label":511},"/","Start Free Scan",[30,513,515],{"id":514},"scan-for-exposed-secrets","Scan for Exposed Secrets",[13,517,518],{},"Check your codebase for accidentally committed secrets.",{"title":51,"searchDepth":520,"depth":520,"links":521},2,[522,523,524,525,526,531,532,533,534,535,536],{"id":32,"depth":520,"text":33},{"id":63,"depth":520,"text":64},{"id":79,"depth":520,"text":80},{"id":95,"depth":520,"text":96},{"id":186,"depth":520,"text":187,"children":527},[528,530],{"id":191,"depth":529,"text":192},3,{"id":204,"depth":529,"text":205},{"id":217,"depth":520,"text":218},{"id":278,"depth":520,"text":279},{"id":309,"depth":520,"text":310},{"id":325,"depth":520,"text":326},{"id":463,"depth":520,"text":464},{"id":514,"depth":520,"text":515},"best-practices","2026-01-22","Environment variable security best practices. Learn to manage secrets, configure applications securely, and avoid common env var mistakes across platforms.",false,"md",[543,544,545,546],{"question":439,"answer":442},{"question":445,"answer":448},{"question":451,"answer":454},{"question":457,"answer":460},"vibe-green",null,{},true,"Secure your application secrets with proper environment variable management.","/blog/best-practices/environment-variables","11 min read","[object Object]","Article",{"title":5,"description":539},{"loc":552},"blog/best-practices/environment-variables",[],"summary_large_image","2FmougQvIqV-JDJ3xxxtYOx9LOdr4-LXPRRKS9oKRuw",1775843918547]