Build a production-shaped app
Protect environment values
Keep server configuration outside source control and understand exactly when a value becomes part of browser-visible code.
8 minute lesson
Next.js loads environment files for local work. Values without the NEXT_PUBLIC_ prefix remain server-only when used in server code.
A public prefix deliberately inlines a value into browser code at build time, so it must never hold a secret. The public value is frozen when the app is built: promoting the same artifact to another environment does not replace it at runtime.
Keep .env.local ignored, provide a documented .env.example with names but no values, and configure real values in the deployment platform. Validate required configuration near the server entry point rather than failing deep inside a request.
// lib/server/config.ts
import 'server-only'
const notesApiToken = process.env.NOTES_API_TOKEN
if (!notesApiToken) throw new Error('NOTES_API_TOKEN is required')
export const serverConfig = { notesApiToken }
server-only turns an accidental Client Component import into a build error. It is a guardrail, not a vault. A value still leaks if server code renders it into HTML, passes it as a client prop, includes it in an error, or logs it somewhere other people can read.
Add NOTES_API_TOKEN through the guarded config module and its empty name to .env.example. Deliberately import the module from a Client Component to observe the failure, then remove the import. Inspect Git, HTML, client props, built JavaScript, and production logs for the token value.
Lesson completed