Getting Started
Core Concepts
Troubleshooting
Implementing robust authentication and security practices protects user accounts, private routes, and sensitive application data. This guide covers how to handle login flows using cookies and server actions, secure private pages, protect against cross-site request vulnerabilities, and configure security headers.
User login flows typically rely on setting and managing secure HTTP cookies. Cookies handle session state between the browser and the server.
Important
The cookies() API returns a Promise and must be unwrapped using await or React.use() before you can read or check its properties. Accessing cookie properties synchronously will result in a runtime error.
You can securely set, update, or clear authentication cookies inside Server Actions and Route Handlers.
import { cookies } from 'next/headers'
export async function POST(request: Request) {
const cookieStore = await cookies()
// Set an authentication cookie upon successful login
cookieStore.set({
name: 'session_token',
value: 'encrypted-user-session-string',
httpOnly: true,
path: '/',
secure: process.env.NODE_ENV !== 'development',
sameSite: 'lax',
})
return new Response('Logged in successfully', { status: 200 })
}Warning
Cookies can only be modified inside a Server Action or Route Handler. Attempting to call mutation methods like .set() or .delete() directly inside a standard Server Component rendering phase will throw an error.
To secure private routes and restrict unauthorized users from viewing sensitive data, you can check user sessions and trigger authorization interrupts.
When a user attempts to access a restricted resource without proper credentials, you can trigger an unauthorized response.
Note
The unauthorized() utility is currently an experimental feature and requires enabling authInterrupts in your configuration.
Supported contexts for invoking authorization helpers:
To protect your application from common web vulnerabilities like Cross-Site Request Forgery (CSRF) and cache poisoning, the server implements strict validation checks.
During development, cross-origin access to internal resources is blocked by default for safety. If your setup requires loading the development server from a custom host or separate origin, you must add that host to your allowed origins list.
Tip
To allow specific hosts to communicate with your development server securely, configure the allowedDevOrigins setting in your project configuration file.
module.exports = {
allowedDevOrigins: ['my-custom-domain.local'],
}Server Actions utilize encryption keys to secure data payloads. By default, an encryption key is automatically generated and managed in your local cache directory. If you need to supply a custom encryption key across deployments, use the designated environment variable:
NEXT_SERVER_ACTIONS_ENCRYPTION_KEYConfiguring security headers ensures that your application defends against browser-level attacks, clickjacking, and MIME-type sniffing. You can define custom headers in your project configuration.
Caution
Setting custom Cache-Control headers on internal Next.js assets (/_next/) can break default development behavior and production caching mechanisms. Avoid overriding cache rules on internal system routes.