Environment variables
Every key in .env.local, what it is for, and what breaks if it is missing.
Every key lives in .env.local, created by copying .env.example, and never
committed. Brand identity does not go here: that is in
src/config/product.ts.
The prefix rule
Only variables prefixed NEXT_PUBLIC_ reach the browser. Anyone can read
them by viewing the page source. Everything else stays on the server and never
leaves it.
That is why the Stripe publishable key carries the prefix and the secret one
does not. If you are ever unsure whether a key can take NEXT_PUBLIC_, the
answer is no.
Site
| Variable | What it is for |
|---|---|
NEXT_PUBLIC_BASE_URL |
Your real public URL. The sitemap, the canonical addresses, the links inside emails and the legal pages all hang off it. If it does not match your domain, your search visibility points somewhere else. |
NEXT_PUBLIC_APP_NAME |
The public application name, read by some metadata. |
Firebase, browser side
The six NEXT_PUBLIC_FIREBASE_* values come from the Firebase console, in the
project settings, when you create a web app: API_KEY, AUTH_DOMAIN,
PROJECT_ID, STORAGE_BUCKET, MESSAGING_SENDER_ID and APP_ID.
Their being public is normal and not a security flaw: what protects your data is the Firestore rules, not the secrecy of these keys.
Firebase, server side
FIREBASE_ADMIN_PROJECT_ID, FIREBASE_ADMIN_CLIENT_EMAIL and
FIREBASE_ADMIN_PRIVATE_KEY come from a service account.
The private key goes in quotes with its line breaks written as \n. This is
the most common deployment mistake: pasted as-is, with real line breaks, startup
fails.
These three grant full access, bypassing the rules. Treat them like your bank password.
Stripe
| Variable | What it is for |
|---|---|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
The publishable key, the only one the browser sees. |
STRIPE_SECRET_KEY |
The server-side secret key. |
STRIPE_WEBHOOK_SECRET |
The webhook signing secret. Without it the route cannot tell a real Stripe notification from an invented one, and rejects it. |
The signing secret is different in test and in production. Moving the webhook to the production domain and forgetting to change this value is what makes payments stop confirming after a deploy.
| Variable | What it is for |
|---|---|
ZOHO_EMAIL |
The mailbox messages are sent from. The template uses Zoho SMTP, and adapts to another provider by changing the transport. |
ZOHO_EMAIL_PASSWORD |
Its application password. |
SUPPORT_EMAIL |
Where support and contact notifications arrive. It can differ from the one above. |
Administration
ADMIN_EMAILS is a comma-separated list of email addresses. It is the only
thing that grants panel access. Anyone whose address is listed, signing in
with that address, sees the panel button on their profile; anyone else is
refused by the server even if they type the URL by hand.
There is no "is admin" field in the database for someone to tamper with: the permission lives in your deployment configuration.
Security and privacy
| Variable | What it is for |
|---|---|
IP_SALT |
Salt used to hash IP addresses before storing them. Minimum 16 characters. Lets you rate-limit by IP without keeping the IP in the clear. |
EMAIL_HASH_SALT |
Salt for hashing emails in the GDPR deletion records, which therefore hold no personal data. |
UNSUBSCRIBE_SECRET |
Signs unsubscribe links, so nobody can unsubscribe someone else by changing an ID in the URL. |
All three ship with change-me-* values. Change them before deploying and
generate them at random. Left as they are, anyone holding the template knows
your salts.