By framework
Next.js security — Server Actions, environment variables, and dependency CVEs
Next.js incidents happen at the server/client boundary: env-var exposure (server-only secrets shipped to the client), missing authorization in Server Actions, and known dependency CVEs. Keep secrets server-only, scope by owner, monitor CVEs. Defensive, no attack steps.
For: anyone running a Next.js app. No attack steps here — just the incidents that happen at the server/client boundary and how to close them. For the full picture, see the security-by-framework hub.
The big three pitfalls (at the boundary)
Next.js's escaping and routing are excellent, but these three are yours to close by minding the boundary.
① Env-var exposure
Misused NEXT_PUBLIC_ or secrets passed to the client. Baked into the bundle, visible to visitors.
② Action authorization gaps
No owner check in Server Actions / Route Handlers. Swap an ID, operate on another user's data.
③ Known dependency CVEs
Core/dependency CVEs (incl. RCE) left unpatched. Judge by the running version, patch fast.
How to close them (3 steps)
Keep secrets server-only
NEXT_PUBLIC_; never on API keys or connection info. Read secrets only inside server components / Server Actions / Route Handlers, and keep them out of props and responses. (→ .env files and secrets)Authorize in every Server Action / Route Handler
Machine-monitor dependency CVEs and patch fast
Common (dangerous)
- secrets prefixed
NEXT_PUBLIC_, exposed to the client - Server Actions treated as "logged in = allowed"
- fetching external URLs on the server unguarded (SSRF)
- known dependency CVEs left unpatched
Correct
- secrets are server-only, never sent to the client
- actions do owner-scoped authorization
- URL fetches go through SSRF protection (block internal IPs, allowlist)
- dependency CVEs machine-monitored + patched fast
This site's view: manage 'the boundary and dependencies,' not the core
This site runs on Next.js, and the center of gravity we actually guard isn't flashy config but the server/client boundary and dependency freshness. Secrets stay on the server, public entry points (Server Actions / Route Handlers) always carry authorization, and dependencies get a CVE audit before every deploy. Our own origin was an incident of "unpatched CVE auto-exploited," so machine-monitoring dependencies is a top-priority habit. Any code that fetches an external URL goes through our own SSRF-safe gateway so it can't reach internal IPs or metadata (→ what SSRF is).
Read next
- Hub: security by framework · Laravel security
- Dependencies: not falling behind on CVEs · the vulnerability-response playbook
- Glossary: what IDOR is · what SSRF is · .env files and secrets
FAQ
QIs Next.js a secure framework?
Next.js has safe defaults (output escaping, etc.) and is fairly solid out of the box. But incidents come not from the core defaults but from how you handle the server/client boundary: secrets that were meant to be server-only ending up in the client, forgetting authorization in Server Actions, and leaving known dependency CVEs unpatched. You can't rely on defaults alone — you manage the boundary and dependencies yourself.
QHow do I handle environment variables safely?
The rule is 'secrets stay on the server.' Only prefix values with NEXT_PUBLIC_ if they're safe for the browser; never on API keys or connection info (NEXT_PUBLIC_ values are baked into the client bundle at build time and visible to visitors). Read secrets only inside server components / Server Actions / Route Handlers, and design so they never slip into props or responses.
QWhat should I watch for with Server Actions?
Server Actions and Route Handlers are public server entry points. Being callable doesn't mean it's allowed. On top of login (authentication), write authorization that scopes every operation to the user who owns the target. Forget it and someone can update or delete another user's data just by swapping an ID (auth ≠ authz). Validate input, and always do the ownership check in the action.