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.

Email

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.