Environment Variables & next.config
Why environment variables?
Passwords, API keys and URLs change between your laptop and the live server. Hard-coding them is dangerous (they end up on GitHub) and inflexible. Environment variables keep them outside the code.
Hard-coded
- password: 'Str0ng#Pass'
- Pushed to GitHub
- Same value everywhere
- Leaked = hacked
Environment variable
- password: process.env.DB_PASSWORD
- Stays on each machine
- Different per environment
- Easy to rotate
The .env files and their order
.env # defaults for all environments (can be committed if no secrets) .env.development # only for npm run dev .env.production # only for next build / next start .env.local # YOUR secrets – overrides the above – never commit .env.example # template for teammates (committed, fake values)
# .env.local DB_PASSWORD=Str0ng#Pass SESSION_SECRET=ZmJ4b3Y5c2VjcmV0LWtleS1jaGFuZ2UtbWUtMTIzNDU2 SITE_URL=https://edusite.in NEXT_PUBLIC_WHATSAPP=919000000000
Server-only variables
// lib/site.js (EduSite) — read on the server
export const site = {
name: 'EduSite',
url: process.env.SITE_URL || 'http://localhost:3000',
// …
}
// lib/db.js
password: process.env.DB_PASSWORD // undefined in the browser – safeNEXT_PUBLIC_ variables
Only variables starting with NEXT_PUBLIC_ are available in Client Components. Next.js copies their value into the JavaScript bundle, so anyone can see them — never put secrets there.
// components/WhatsAppButton.jsx — Client Component
'use client'
export default function WhatsAppButton() {
const number = process.env.NEXT_PUBLIC_WHATSAPP // replaced with "919000000000" at build
return (
<a href={`https://wa.me/${number}?text=Hi, I want course details`}
target="_blank" rel="noopener" className="btn">
💬 Chat on WhatsApp
</a>
)
}Build time vs runtime
| Where used | Value read at | After changing it… |
|---|---|---|
| NEXT_PUBLIC_ in client code | Build | Rebuild needed |
| Static (○/●) pages | Build | Rebuild needed |
| Dynamic (ƒ) pages, actions, APIs | Each request | Restart server |
That is why EduSite's README says: set SITE_URL before npm run build — the sitemap and canonical URLs are static.
next.config.mjs explained
// next.config.mjs (EduSite)
/** @type {import('next').NextConfig} */
const nextConfig = {
// Simple permanent redirects (no code needed)
async redirects() {
return [{ source: '/home', destination: '/', permanent: true }]
},
images: {
qualities: [75, 90], // quality values the site uses
},
}
export default nextConfigconst nextConfig = {
reactStrictMode: true,
poweredByHeader: false, // hide "X-Powered-By: Next.js"
trailingSlash: false, // /about not /about/
output: 'standalone', // small server bundle for VPS/Docker
async rewrites() {
return [{ source: '/docs/:path*', destination: 'https://docs.example.com/:path*' }]
},
async headers() {
return [{
source: '/(.*)',
headers: [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=()' },
],
}]
},
}Security headers in config
The headers() function above adds protective HTTP headers to every response — a cheap security and "best practices" score boost.
Common mistakes
- Secret with
NEXT_PUBLIC_prefix → exposed to the world. - Forgetting to add variables on the hosting dashboard (Vercel → Settings → Environment Variables).
- Quotes and spaces:
KEY = "value"→ writeKEY=value. - Editing next.config without restarting dev → config is read only at start.
Summary
- Secrets in
.env.local; template in.env.example. - Only
NEXT_PUBLIC_*reaches the browser, inlined at build time. next.config.mjs: redirects, rewrites, headers, images, output.
Add NEXT_PUBLIC_WHATSAPP and a floating WhatsApp button. Add the three security headers and check them in DevTools → Network → Response Headers.
