Env-var hygiene template — Vercel / Netlify / Fly / Railway

Updated

Per-platform env-var setup with NEXT_PUBLIC_ guidance, secret-manager recommendations, and rotation cadence. Drop into your project README + reference from your DPA + SOC 2 evidence.

How to use

Adapt per platform + per environment (dev / staging / prod).

Template (markdown)

copy-paste, replace {{PLACEHOLDERS}}
# Environment Variable Hygiene
**Last updated:** {{DATE}} · **Owner:** {{CTO}}

## 1. Naming convention
- **Server-only secrets** — no prefix
  - \`OPENAI_API_KEY\`, \`STRIPE_SECRET_KEY\`, \`ANTHROPIC_API_KEY\`, \`SUPABASE_SERVICE_ROLE_KEY\`, \`AUTH_SECRET\`
- **Public values** — \`NEXT_PUBLIC_\` (Next.js), \`VITE_\` (Vite), \`PUBLIC_\` (SvelteKit, Astro, etc.)
  - \`NEXT_PUBLIC_SITE_URL\`, \`NEXT_PUBLIC_SUPABASE_URL\`, \`NEXT_PUBLIC_SUPABASE_ANON_KEY\`
  - **NEVER prefix a secret with these** — they ship in the client bundle to every visitor.

## 2. Per-platform setup

### Vercel
- Production secrets: Vercel Dashboard → Project → Settings → Environment Variables → "Sensitive" toggle ON
- Preview vs Production scoping: use the per-environment selector
- Pull locally: \`vercel env pull .env.local\`

### Netlify
- Production secrets: Site → Site Settings → Environment Variables
- Mark as "secret" via the lock icon
- Sensitive scope per branch via the deploy contexts

### Fly.io
- \`fly secrets set OPENAI_API_KEY=...\`
- View: \`fly secrets list\`
- Rotate: \`fly secrets set OPENAI_API_KEY=<new>\` (atomic, no restart needed for most apps)

### Railway
- Project → Variables → Add Variable → Reference (for sharing across services)
- Per-environment scoping via the env selector

### GitHub Actions
- Repo → Settings → Secrets and variables → Actions → New repository secret
- Reference in workflows: \`${{ secrets.OPENAI_API_KEY }}\`

## 3. .gitignore essentials (every project)
\`\`\`
.env
.env.local
.env.production
.env.development
.env.*.local
.claude/
.cursor/
.continue/
.cline/
.codeium/
.windsurf/
\`\`\`

(The dot-directories at the bottom are AI-coding-tool config dirs that frequently capture credentials. April 2026 Bitwarden CLI hijack hunted these specifically.)

## 4. Rotation cadence
- **Payment-touching keys (Stripe live, AWS prod):** every 30 days
- **High-value (OpenAI, Anthropic, Supabase service-role):** every 60 days
- **Standard (vendor APIs, internal services):** every 90 days
- **On team-member departure:** within 4h
- **On suspected leak:** immediately (use rotation-step playbook from /leak/<vendor>)

## 5. Spend caps (every paid AI API)
- **OpenAI:** Dashboard → Limits → set monthly + per-day cap
- **Anthropic:** Console → Settings → set per-project spend cap
- **Stripe:** not applicable (you're the merchant) — use restricted keys instead

## 6. Verification checklist
- [ ] No \`NEXT_PUBLIC_\` / \`VITE_\` / \`PUBLIC_\` prefix on any secret
- [ ] Every key has a documented rotation date
- [ ] Spend cap configured on every paid API
- [ ] Vendor-side audit log enabled for high-value keys
- [ ] Securie's secret_scanner specialist runs on every PR (catches accidental commits)
- [ ] All AI-coding-tool dot-dirs in .gitignore + .npmignore