Next.js Security Best Practices
Next.js blurs the line between frontend and backend. Server components, server actions and route handlers run on the server, right next to client code. That is productive, and it makes it easy to forget which code is a public API.
Server actions and route handlers are public endpoints
Every server action and route handler can be called directly by anyone, not just from your UI. Treat each one like an API endpoint:
"use server";
export async function deleteProject(id: string) {
const session = await getSession();
if (!session) throw new Error("Unauthorized");
const project = await db.project.findFirst({ where: { id, orgId: session.orgId } });
if (!project) throw new Error("Not found");
await db.project.delete({ where: { id: project.id } });
}
- Authenticate in every action and handler.
- Authorise the specific object, scoped to the user or organisation.
- Validate input with a schema; arguments come from the client.
Do not rely on middleware alone for authorisation
Middleware is useful for redirects and coarse checks, but authorisation decisions should also be enforced where data is accessed. Next.js has had a past vulnerability where middleware could be bypassed with a crafted header, which was patched. Keep Next.js updated, and check access in the data layer too, so a middleware gap does not expose data.
Keep secrets out of the client bundle
- Environment variables prefixed with
NEXT_PUBLIC_are embedded in client JavaScript and visible to everyone. Never put secrets there. - Import server-only modules with the
server-onlypackage so accidental client imports fail at build time. - Review what your build outputs to the browser.
Avoid leaking data from server to client components
Passing a full database object as a prop to a client component serialises every field into the page, including fields you did not display, such as emails, internal flags or hashes.
- Pass only the fields the component needs.
- Use data transfer objects or select specific columns.
- Consider React's taint APIs, where available, to prevent sensitive values reaching the client.
Rendering user content safely
React escapes output by default. Risks return with dangerouslySetInnerHTML, Markdown rendering and user-controlled href values. Sanitise HTML and validate URL schemes.
Security headers
Configure headers in next.config.js or middleware: Content Security Policy, preferably nonce-based, HSTS, X-Content-Type-Options, Referrer-Policy and framing protection. See our security headers guide.
Image and fetch features
- Restrict
images.remotePatternsto domains you trust, to avoid your server fetching arbitrary URLs. - Validate any user-supplied URLs fetched on the server to prevent SSRF.
Caching and data exposure
Be careful that personalised responses are not cached and served to other users. Mark user-specific routes as dynamic and send Cache-Control: private, no-store where appropriate.
Keep dependencies current
Next.js and React release security fixes regularly. Enable automated dependency updates and apply security releases quickly.
Next.js security checklist
| Area | Check |
|---|---|
| Server actions and handlers | Authenticated, authorised, validated |
| Middleware | Not the only authorisation layer |
| Environment variables | No secrets with NEXT_PUBLIC_ |
| Server to client props | Minimal fields only |
| User content | Sanitised HTML, safe URLs |
| Headers | CSP, HSTS and others set |
| Remote images | Restricted patterns |
| Caching | No shared caching of personal data |
| Updates | Next.js and React patched |
Testing your Next.js app
SecurityInfinity discovers server actions and API routes, tests them with multiple identities, checks client bundles for exposed secrets and verifies headers and caching. Start free.
Frequently asked questions
Are server actions secure by default?
They include some protections, such as origin checks, but authentication and authorisation are your responsibility in every action.
Is it safe to put API keys in Next.js environment variables?
Yes, if they are not prefixed with NEXT_PUBLIC_ and are only used in server code.
Do I still need CSRF protection in Next.js?
Server actions use POST and check origins, which mitigates CSRF. Custom route handlers using cookies should still verify origin or use tokens.