Cookies y Headers
Lee y establece cookies y headers en el servidor con las APIs cookies() y headers() de next/headers.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
import { cookies, headers } from "next/headers";
// Lectura de cookies (async en Next.js 15+)
export default async function Page() {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value ?? "light";
const token = cookieStore.get("auth-token")?.value;
// Lectura de headers
const headersList = await headers();
const userAgent = headersList.get("user-agent") ?? "";
const ip = headersList.get("x-forwarded-for") ?? "unknown";
return <div>Tema: {theme}</div>;
}
// Establecimiento de cookies en un Server Action
"use server";
import { cookies } from "next/headers";
export async function setTheme(theme: string) {
const cookieStore = await cookies();
cookieStore.set("theme", theme, {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: 60 * 60 * 24 * 365, // 1 year
});
}Cuándo usarlo: Necesitas leer tokens de autenticación, preferencias de localización, feature flags, u otros datos por solicitud de cookies o headers.
Ejemplo Práctico
// app/layout.tsx (Server Component)
import { cookies, headers } from "next/headers";
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value ?? "system";
const locale = cookieStore.get("locale")?.value ?? "en";
const headersList = await headers();
const acceptLanguage = headersList.get("accept-language");
return (
<html lang={locale} data-theme={theme}>
<body>{children}</body>
</html>
);
}// app/actions/preferences.ts
"use server";
import { cookies } from "next/headers";
import { revalidatePath } from "next/cache";
export async function setTheme(formData: FormData) {
const theme = formData.get("theme") as string;
if (!["light", "dark", "system"].includes(theme)) {
return { error: "Tema inválido" };
}
const cookieStore = await cookies();
cookieStore.set("theme", theme, {
httpOnly: false, // allow client JS to read for immediate UI update
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 365,
});
revalidatePath("/", "layout");
}
export async function setLocale(locale: string) {
const cookieStore = await cookies();
cookieStore.set("locale", locale, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 365,
});
revalidatePath("/", "layout");
}// app/components/theme-switcher.tsx
"use client";
import { useTransition } from "react";
import { setTheme } from "@/app/actions/preferences";
export function ThemeSwitcher({ currentTheme }: { currentTheme: string }) {
const [isPending, startTransition] = useTransition();
const themes = ["light", "dark", "system"] as const;
return (
<div className="flex gap-2">
{themes.map((theme) => (
<button
key={theme}
onClick={() =>
startTransition(async () => {
const fd = new FormData();
fd.set("theme", theme);
await setTheme(fd);
})
}
className={`px-3 py-1 rounded border ${
currentTheme === theme
? "bg-blue-600 text-white"
: "bg-white text-gray-700"
} ${isPending ? "opacity-50" : ""}`}
disabled={isPending}
>
{theme}
</button>
))}
</div>
);
}// middleware.ts -- lectura y establecimiento de cookies/headers en Middleware
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// Lee una cookie
const locale = request.cookies.get("locale")?.value ?? "en";
// Lee un header
const country = request.headers.get("x-vercel-ip-country") ?? "US";
// Establece un header para uso posterior
const response = NextResponse.next();
response.headers.set("x-locale", locale);
response.headers.set("x-country", country);
// Establece una cookie
if (!request.cookies.has("visitor-id")) {
response.cookies.set("visitor-id", crypto.randomUUID(), {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}Lo que esto demuestra:
- Lectura de cookies y headers en Server Components y layouts
- Establecimiento de cookies en Server Actions con opciones de seguridad adecuadas
- Uso de Middleware para leer/establecer cookies y headers en el edge
- Un patrón de cambiador de tema que combina cookies del servidor con interactividad del cliente
Análisis Profundo
Cómo Funciona
cookies()yheaders()son funciones async en Next.js 15+ que devuelven el almacén de cookies y el mapa de headers para la solicitud actual.- Llamar a cualquiera de estas funciones convierte la ruta en renderizado dinámico — la página no puede ser generada estáticamente porque depende de datos en tiempo de solicitud.
cookies()devuelve un objetoReadonlyRequestCookiescuando se lee en Server Components. En Server Actions y Route Handlers, devuelve un almacén escribible que admite.set()y.delete().headers()devuelve un objetoHeadersde solo lectura. No puedes establecer response headers desde un Server Component — usa Middleware o Route Handlers para eso.- Las cookies establecidas en Server Actions se envían de vuelta como response headers
Set-Cookie. El navegador las aplica inmediatamente. - Middleware se ejecuta antes de todo manejo de rutas y puede modificar tanto cookies/headers de solicitud como de respuesta.
Variaciones
Eliminar una cookie:
"use server";
import { cookies } from "next/headers";
export async function logout() {
const cookieStore = await cookies();
cookieStore.delete("auth-token");
cookieStore.delete("session");
}Leer todas las cookies:
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
// [{ name: "theme", value: "dark" }, { name: "locale", value: "en" }]Verificar si una cookie existe:
const cookieStore = await cookies();
const hasAuth = cookieStore.has("auth-token");Establecer response headers personalizados en un Route Handler:
// app/api/data/route.ts
import { NextResponse } from "next/server";
export async function GET() {
const data = await fetchData();
return NextResponse.json(data, {
headers: {
"Cache-Control": "public, max-age=3600",
"X-Custom-Header": "my-value",
},
});
}Notas de TypeScript
import { cookies, headers } from "next/headers";
// cookies() devuelve Promise<ReadonlyRequestCookies> en Server Components
// y Promise<RequestCookies> en Server Actions (escribible)
const cookieStore = await cookies();
const value: string | undefined = cookieStore.get("key")?.value;
// headers() devuelve Promise<ReadonlyHeaders>
const headersList = await headers();
const value: string | null = headersList.get("x-custom");
// Tipo de opciones de cookie
type CookieOptions = {
name: string;
value: string;
domain?: string;
path?: string;
maxAge?: number;
expires?: Date;
httpOnly?: boolean;
secure?: boolean;
sameSite?: "strict" | "lax" | "none";
};Trampas
-
cookies()yheaders()hacen la ruta dinámica — Cualquier Server Component o layout que llame a estas funciones no puede ser generado estáticamente. Solución: Si necesitas el valor de la cookie solo para interactividad, léelo en el cliente condocument.cookieo una librería comojs-cookieen su lugar. -
No puedes establecer cookies en Server Components — El almacén de cookies es de solo lectura fuera de Server Actions y Route Handlers. Solución: Usa un Server Action para establecer cookies, o establécelas en Middleware.
-
cookies()es async en Next.js 15+ — Llamar acookies()sinawaitdevuelve una Promise, no el almacén de cookies. Solución: Siempreconst cookieStore = await cookies(). -
Cookies de Middleware vs cookies de Server Component — Las cookies establecidas en Middleware a través de
response.cookies.set()están disponibles para Server Components posteriores a través decookies()en la misma solicitud. Sin embargo, las cookies establecidas en Server Actions solo están disponibles en la siguiente solicitud. Solución: Entiende el ciclo de vida de la solicitud; usa Middleware para la inyección de cookies por solicitud. -
sameSite: "none"requieresecure: true— Los navegadores rechazan las cookiesSameSite=Nonesin el flagSecure. Solución: Siempre emparejasameSite: "none"consecure: true. -
Límites de tamaño de cookies — Los navegadores limitan las cookies individuales a aproximadamente 4 KB y el total de cookies por dominio a aproximadamente 80. Solución: Almacena datos mínimos en cookies; usa un ID de sesión que apunte al almacenamiento del lado del servidor para cargas útiles grandes.
Alternativas
| Alternativa | Úsalo Cuando | No lo Usos Cuando |
|---|---|---|
| Cookies de Middleware | Necesitas leer o establecer cookies antes del manejo de rutas | Solo necesitas cookies dentro de un Server Action |
document.cookie (cliente) | Necesitas leer una cookie no-httpOnly para actualizaciones inmediatas de UI | Necesitas acceso del lado del servidor o cookies httpOnly |
Librería js-cookie | Quieres una API más amigable para la gestión de cookies del lado del cliente | Estás trabajando en Server Components |
| Almacenamiento de sesión (p. ej., iron-session) | Necesitas datos de sesión encriptados y a prueba de manipulaciones | Las preferencias simples de clave-valor son suficientes |
searchParams | Los datos deben ser visibles en la URL y compartibles | Los datos son sensibles o específicos del usuario |
FAQs
¿Por qué llamar a cookies() o headers() hace una ruta dinámica?
- Estas funciones dependen de datos en tiempo de solicitud que son diferentes para cada visitante
- La página no puede ser generada estáticamente porque los valores de cookie/header son desconocidos en tiempo de compilación
- Si solo necesitas una cookie para interactividad, léela en el cliente con
document.cookieen su lugar
¿Por qué deben cookies() y headers() ser awaited en Next.js 15+?
- En Next.js 15+, ambas son funciones async que devuelven Promises
- Llamar a
cookies()sinawaitte da un objeto Promise, no el almacén de cookies - Siempre usa
const cookieStore = await cookies()
¿Puedes establecer cookies dentro de un Server Component?
- No. El almacén de cookies es de solo lectura en Server Components y layouts
- Usa un Server Action o Route Handler para establecer cookies
- Alternativamente, establece cookies en Middleware
¿Cuál es la diferencia entre cookies establecidas en Middleware vs Server Actions?
- Las cookies establecidas en Middleware están disponibles para Server Components posteriores en la misma solicitud
- Las cookies establecidas en Server Actions se envían como response headers
Set-Cookiey solo están disponibles en la siguiente solicitud - Usa Middleware para la inyección de cookies por solicitud
¿Cómo eliminas una cookie en un Server Action?
"use server";
import { cookies } from "next/headers";
export async function logout() {
const cookieStore = await cookies();
cookieStore.delete("auth-token");
cookieStore.delete("session");
}¿Qué sucede si usas sameSite: "none" sin secure: true?
- Los navegadores rechazan la cookie por completo
SameSite=Nonerequiere que el flagSecuresea establecido- Siempre empareja
sameSite: "none"consecure: true
¿Cómo estableces response headers personalizados en Next.js?
- No puedes establecer response headers desde un Server Component
- Usa un Route Handler con
NextResponse.json(data, { headers: {...} }) - O usa Middleware con
response.headers.set("key", "value")
¿Cuál es la diferencia de tipo de TypeScript entre cookies en Server Components vs Server Actions?
// Server Component: ReadonlyRequestCookies (solo lectura)
const cookieStore = await cookies();
cookieStore.get("key"); // OK
// cookieStore.set(...) // Error
// Server Action: RequestCookies (escribible)
const cookieStore = await cookies();
cookieStore.set("key", "value", { httpOnly: true }); // OK¿Cuáles son los límites del navegador en cookies?
- Las cookies individuales se limitan a aproximadamente 4 KB
- El total de cookies por dominio se limita aproximadamente a 80
- Almacena datos mínimos en cookies; usa un ID de sesión que apunte al almacenamiento del lado del servidor para cargas útiles grandes
¿Cómo escribes el objeto de opciones de cookie en TypeScript?
type CookieOptions = {
name: string;
value: string;
domain?: string;
path?: string;
maxAge?: number;
expires?: Date;
httpOnly?: boolean;
secure?: boolean;
sameSite?: "strict" | "lax" | "none";
};¿Cómo lees todas las cookies a la vez?
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
// [{ name: "theme", value: "dark" }, { name: "locale", value: "en" }]¿Por qué deberías establecer httpOnly: false para una cookie de tema pero httpOnly: true para un token de auth?
httpOnly: falsepermite que JavaScript del cliente lea la cookie para actualizaciones inmediatas de UI (p. ej., tema)httpOnly: truepreviene el acceso de JavaScript, protegiendo tokens sensibles de ataques XSS- Siempre usa
httpOnly: truepara cookies de autenticación y sesión
Relacionado
- Server Actions -- Establecimiento de cookies después de mutaciones
- Search Params -- Datos en tiempo de solicitud alternativos a través de URL
- Fetching -- Cómo cookies/headers convierten rutas en renderizado dinámico
- Static vs Dynamic -- Funciones dinámicas y modo de renderizado