How to Secure Next.js
Next.js is one of the most popular React frameworks, but its flexibility means security is not automatic. This guide covers the areas that matter most when shipping a Next.js application to production.
Server Components and Data Exposure
Next.js App Router uses React Server Components by default. Server Components run only on the server, but data they pass to Client Components is serialized and sent to the browser.
- Never pass secrets, database records, or internal identifiers as props to Client Components.
- Use the
server-onlypackage (Next.js docs — server-only) to prevent accidental import of server modules in client code.
npm install server-only
import "server-only";
export function getDbConnection() {
// This module will throw a build error if imported in a Client Component
}
Content Security Policy
CSP is critical for preventing XSS. Next.js does not set one by default.
See also: Next.js CSP Configuration for the full setup.
Configure CSP via next.config.ts headers or middleware. A strict starting point:
// proxy.ts (middleware.ts)
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const isDev = process.env.NODE_ENV === "development";
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${isDev ? " 'unsafe-eval'" : ""};
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`;
const contentSecurityPolicyHeaderValue = cspHeader
.replace(/\s{2,}/g, " ")
.trim();
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
requestHeaders.set("Content-Security-Policy", contentSecurityPolicyHeaderValue);
const response = NextResponse.next({
request: { headers: requestHeaders },
});
response.headers.set("Content-Security-Policy", contentSecurityPolicyHeaderValue);
return response;
}
Reference: Next.js — Content Security Policy
Security Headers
Set these headers in next.config.ts or middleware:
// next.config.ts
const securityHeaders = [
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-XSS-Protection", value: "0" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
];
const nextConfig = {
async headers() {
return [
{
source: "/(.*)",
headers: securityHeaders,
},
];
},
};
export default nextConfig;
Reference: Next.js — Headers
SSRF Protection
Server Actions, Route Handlers, and fetch() calls in Server Components all run on the server. If any of these accept user-supplied URLs, you are exposed to SSRF.
See also: Next.js SSRF Protection for the full guide.
Key points:
- Never pass user input directly to
fetch()on the server. - Validate URLs against an allowlist of domains.
- Block private IP ranges (
10.x,172.16-31.x,192.168.x,169.254.169.254).
Authentication
Next.js does not provide authentication. Use established libraries:
- NextAuth.js / Auth.js — session-based auth with multiple providers.
- Clerk — hosted auth with a Next.js SDK.
- Supabase Auth — open-source auth with a Next.js integration.
Whichever you choose:
- Store session tokens in HttpOnly, Secure, SameSite=Lax cookies.
- Validate sessions server-side on every request (middleware or layout).
- Protect Server Actions with session checks — they are POST endpoints and can be called directly.
// app/actions.ts
"use server";
import { getServerSession } from "next-auth";
export async function sensitiveAction() {
const session = await getServerSession();
if (!session) {
throw new Error("Unauthorized");
}
// proceed
}
Server Actions Security
Server Actions create POST endpoints automatically. Treat them like API routes:
- Always validate and sanitize input. Use a schema library like Zod.
- Always check authentication and authorization.
- Never trust the client — Server Actions can be called by anything that sends a POST.
"use server";
import { z } from "zod";
const schema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(1).max(10000),
});
export async function createPost(formData: FormData) {
const parsed = schema.safeParse({
title: formData.get("title"),
body: formData.get("body"),
});
if (!parsed.success) {
throw new Error("Invalid input");
}
// proceed with parsed.data
}
Reference: Next.js — Server Actions and Mutations
Environment Variables
Next.js exposes any variable prefixed with NEXT_PUBLIC_ to the browser bundle.
- Never put secrets, API keys, or database credentials in
NEXT_PUBLIC_variables. - Use server-only environment variables for anything sensitive.
- Validate that required variables exist at build time.
Reference: Next.js — Environment Variables
API Route / Route Handler Hardening
- Validate HTTP methods explicitly.
- Rate limit endpoints (see Rate Limiting in Node.js).
- Return minimal error information — never expose stack traces.
- Validate
Content-Typeheaders on POST/PUT/PATCH.
// app/api/data/route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const contentType = request.headers.get("content-type");
if (!contentType?.includes("application/json")) {
return NextResponse.json({ error: "Invalid content type" }, { status: 415 });
}
// proceed
}
Dependency Management
- Run
npm auditregularly. - Pin dependency versions or use a lockfile.
- Use Dependabot or Renovate for automated updates.
- Audit transitive dependencies — most vulnerabilities are in sub-dependencies.
Reference: npm — audit
Deployment Hardening
- Disable Next.js telemetry in production:
NEXT_TELEMETRY_DISABLED=1. - Remove the
X-Powered-Byheader: setpoweredByHeader: falseinnext.config.ts. - Use standalone output for minimal container images.
// next.config.ts
const nextConfig = {
poweredByHeader: false,
output: "standalone",
};
export default nextConfig;
Reference: Next.js — Deploying
Related Guides
Content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly, send corrections to [email protected]. Always consult official documentation and validate key implementation decisions before making design or security choices.
