Security is an essential part of building a production-ready Next.js application. As a Senior Frontend Developer, I focus on protecting user accounts, validating incoming data, securing API endpoints, and preventing common web attacks.
Authentication verifies who the user is, while authorization determines what the user is allowed to do.
Use a trusted authentication solution and check permissions on the server before accessing protected resources.
const session = await auth();if (!session?.user) {return Response.json({ message: "Unauthorized" },{ status: 401 });}if (session.user.role !== "admin") {return Response.json({ message: "Forbidden" },{ status: 403 });}
Best practices:
proxy.ts.Never trust data received from the client. Users can bypass frontend validation and send requests directly to your server.
Use Zod to validate incoming data and protect Route Handlers and Server Actions with authentication and authorization checks.
import { z } from "zod";const schema = z.object({email: z.string().email(),name: z.string().trim().min(1).max(100),});const result = schema.safeParse(body);if (!result.success) {return Response.json({ message: "Invalid input" },{ status: 400 });}
Best practices:
Protect authentication sessions and prevent malicious scripts or cross-site requests from compromising users.
Configure cookies with appropriate security attributes:
response.cookies.set("session", sessionToken, {httpOnly: true,secure: process.env.NODE_ENV === "production",sameSite: "lax",path: "/",});
For XSS protection, avoid rendering untrusted HTML using dangerouslySetInnerHTML. If HTML rendering is necessary, sanitize the content first.
For CSRF protection, use appropriate SameSite settings, validate request origins where suitable, and implement CSRF tokens when required by your authentication architecture.
Best practices:
HttpOnly, Secure, and appropriate SameSite cookie settings.HttpOnly does not prevent malicious scripts from making requests using an active session.Keep sensitive credentials on the server and prevent excessive requests to critical endpoints.
In Next.js, environment variables prefixed with NEXT_PUBLIC_ can be exposed to browser code.
DATABASE_URL=...API_SECRET=...NEXT_PUBLIC_API_URL=https://api.example.com
Keep database credentials and private API keys in server-only code. Never expose them through client components.
Rate limiting helps prevent brute-force login attempts, password-reset abuse, and excessive API usage. For applications running multiple instances, use a shared solution such as Redis.
Best practices:
429 Too Many Requests when a rate limit is exceeded.Security headers provide additional browser-level protection, while automated tests and monitoring help identify security problems over time.
Common security headers include:
Content-Security-Policy: Restricts permitted sources for scripts and other resources.X-Content-Type-Options: nosniff: Helps prevent MIME-type sniffing.Strict-Transport-Security: Enforces HTTPS after the browser receives the policy.Referrer-Policy: Controls how much referrer information is shared.In addition, test security boundaries and monitor suspicious activity.
Best practices:
next.config.ts or your deployment infrastructure.