What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →"It works on my machine" is usually a config problem. A practical guide to .env files, public vs private variables, validation at startup, and keeping secrets out of your repo.
A surprising number of production bugs aren't code bugs at all. The code is fine; it's reading the wrong database URL, a missing API key, or a variable that silently came through as undefined. Getting environment variables right is one of those boring skills that saves you from very exciting outages.
The idea is simple: the same code should run in development, staging and production, and only the configuration changes. Database URLs, API keys, feature flags, third-party credentials: all of it comes from the environment, not from constants buried in your source.
That gives you three wins:
A .env file is just a convenient way to load variables during local development:
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
STRIPE_SECRET_KEY=sk_test_xxx
NEXT_PUBLIC_SITE_URL=http://localhost:3000
The rules that keep it safe:
.env to .gitignore on day one, before your first commit. Not after..env.example with every key and a fake or empty value. New teammates (and future you) can copy it and fill it in..env in production. Set real values in your host's dashboard (Vercel, Netlify, Render, your VPS's process manager, and so on).Frontend frameworks bundle some variables into the JavaScript that ships to the browser. Anything in that bundle is public. Anyone can open DevTools and read it.
Each framework marks browser-exposed variables with a prefix:
| Framework | Exposed to the browser |
|---|---|
| Next.js | NEXT_PUBLIC_* |
| Vite | VITE_* |
| Create React App | REACT_APP_* |
| Nuxt | values under runtimeConfig.public |
So NEXT_PUBLIC_STRIPE_SECRET_KEY is a contradiction. If a value must stay secret, keep it on the server and call it from an API route or server function. The browser gets the result, never the key.
A good habit: when you add a prefix, say out loud "I'm fine with the whole internet reading this." If that sentence feels wrong, drop the prefix.
The worst config bugs are the silent ones. process.env.API_URL is undefined, your fetch goes to undefined/users, and you lose an afternoon. Fail fast instead. Check required variables at startup and crash with a clear message:
// config.js
import { z } from 'zod'
const schema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
})
export const config = schema.parse(process.env)
Now a missing or malformed variable stops the deploy immediately instead of breaking a random request at 2 a.m. You don't need Zod for this; a ten-line function that throws on missing keys works too. The point is that the check exists.
Bonus: import config everywhere instead of touching process.env directly. You get one place to see every setting the app depends on.
FEATURE_ENABLED=false is the string "false", which is truthy in JavaScript. Parse booleans and numbers explicitly..env once at startup. And browser-exposed variables are baked in at build time, so changing them on your host means redeploying.#, spaces or $ may need quoting depending on the loader. If a password "doesn't work," print its length (not the value) to check it's arriving intact.For a solo project, your host's encrypted environment settings are perfectly fine. As things grow, look at a dedicated secret manager (your cloud provider's offering, or tools like Doppler or Infisical) so you can rotate keys and audit who accessed what.
Whatever you use, follow two rules: give each environment its own credentials, and rotate a key the moment you suspect it leaked. If that has already happened, here is exactly what to do after leaking an API key.
No. Private repos get cloned to laptops, shared with contractors, connected to CI services and occasionally made public by accident. Commit .env.example and keep real values in your host or a secret manager.
Either it lacks your framework's public prefix (by design, since it's server-only), or you changed it without rebuilding. Browser-exposed variables are inlined at build time.
Their public (anon) keys are designed to be exposed, but only because access is controlled by security rules or row-level security. Those rules are what actually protect your data, so make sure they're turned on and tested.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →