Пошук уроків, статей та іншого контенту
Працюйте із заголовками запитів і відповідей, а також читайте та встановлюйте cookies.
У Next.js заголовки HTTP-запитів і cookies можна читати на сервері, а заголовки відповіді та cookies — встановлювати під час формування відповіді.
У сучасному Next.js з App Router для цього використовуються:
headers() — читання заголовків вхідного запиту;
cookies() — читання та зміна cookies;
NextResponse — формування відповіді із заголовками та cookies.
У Next.js 15 API headers() і cookies() є асинхронними, тому їх потрібно викликати з await.
Функція headers() повертає об’єкт Headers, який містить заголовки поточного HTTP-запиту.
import { headers } from 'next/headers';
export default async function Page() {
const requestHeaders = await headers();
const userAgent = requestHeaders.get('user-agent');
const language = requestHeaders.get('accept-language');
return (
<main>
<p>User-Agent: {userAgent ?? 'невідомо'}</p>
<p>Мова браузера: {language ?? 'невідомо'}</p>
</main>
);
}Для отримання значення використовується метод get():
const userAgent = requestHeaders.get('user-agent');Якщо такого заголовка немає, get() повертає null.
Також можна перевірити наявність заголовка:
const hasAuthorization = requestHeaders.has('authorization');Назви HTTP-заголовків не залежать від регістру:
requestHeaders.get('User-Agent');
requestHeaders.get('user-agent');Обидва варіанти звертаються до одного заголовка.
Виклик headers() залежить від конкретного HTTP-запиту. Тому сторінка, яка використовує headers(), вважається динамічною.
Це означає, що її результат не можна безпосередньо підготувати один раз під час збірки, адже заголовки можуть бути різними для кожного відвідувача.
Для роботи з cookies використовується cookies() з пакета next/headers.
import { cookies } from 'next/headers';
export default async function Page() {
const cookieStore = await cookies();
const theme = cookieStore.get('theme')?.value;
const sessionId = cookieStore.get('session_id')?.value;
return (
<main>
<p>Тема: {theme ?? 'не встановлена'}</p>
<p>Сесія: {sessionId ?? 'не встановлена'}</p>
</main>
);
}Метод get() повертає об’єкт із властивостями:
name — назва cookie;
value — значення cookie.
Якщо cookie не існує, результатом буде undefined, тому часто використовують optional chaining:
const value = cookieStore.get('theme')?.value;const cookieStore = await cookies();
if (cookieStore.has('session_id')) {
console.log('Користувач має session_id');
}const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
for (const cookie of allCookies) {
console.log(cookie.name, cookie.value);
}Cookie встановлюється як частина HTTP-відповіді. Тому змінювати cookies можна в місцях, де Next.js формує відповідь:
у Route Handler;
у Server Action.
У Server Component cookies можна читати, але не можна змінювати.
Створимо файл app/api/session/route.ts:
import { cookies, headers } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET() {
const requestHeaders = await headers();
const cookieStore = await cookies();
const requestId =
requestHeaders.get('x-request-id') ?? crypto.randomUUID();
const currentTheme = cookieStore.get('theme')?.value ?? 'light';
const response = NextResponse.json({
message: 'Дані сесії отримано',
theme: currentTheme,
requestId,
});
// Додаємо власний заголовок до HTTP-відповіді
response.headers.set('x-request-id', requestId);
// Встановлюємо cookie для браузера
response.cookies.set('theme', currentTheme, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 30,
});
return response;
}Після запиту до /api/session браузер отримає:
JSON-відповідь;
заголовок x-request-id;
cookie theme.
Параметри cookie в прикладі означають:
httpOnly: true — cookie недоступна через document.cookie у браузері;
secure: true — cookie передається тільки через HTTPS;
sameSite: 'lax' — обмежує автоматичне передавання cookie для міжсайтових запитів;
path: '/' — cookie доступна для всіх шляхів сайту;
maxAge — час життя cookie у секундах.
Для cookie із сесійним ідентифікатором зазвичай використовують httpOnly: true, щоб JavaScript на сторінці не міг прочитати це значення.
Заголовки відповіді потрібно додавати до об’єкта Response або NextResponse.
import { NextResponse } from 'next/server';
export async function GET() {
const response = NextResponse.json({
status: 'ok',
});
response.headers.set('Cache-Control', 'no-store');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
}У цьому прикладі:
Cache-Control: no-store забороняє кешування відповіді;
X-Content-Type-Options: nosniff повідомляє браузеру не намагатися вгадувати тип вмісту.
Для встановлення кількох значень одного заголовка можна використовувати append():
const response = NextResponse.json({ status: 'ok' });
response.headers.append('Vary', 'Accept-Language');
response.headers.append('Vary', 'Cookie');
return response;У більшості випадків для звичайного заголовка достатньо set().
Нижче наведено завершений Route Handler, який:
читає заголовок accept-language;
читає cookie theme;
створює ідентифікатор запиту;
додає ідентифікатор до відповіді;
встановлює cookie.
Файл app/api/context/route.ts:
import { cookies, headers } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET() {
const requestHeaders = await headers();
const cookieStore = await cookies();
const language =
requestHeaders.get('accept-language')?.split(',')[0] ?? 'uk';
const theme = cookieStore.get('theme')?.value ?? 'light';
const requestId =
requestHeaders.get('x-request-id') ?? crypto.randomUUID();
const response = NextResponse.json({
language,
theme,
requestId,
});
// Заголовки відповіді потрібно встановлювати до її повернення
response.headers.set('x-request-id', requestId);
response.headers.set('Cache-Control', 'private, no-store');
// Cookie буде збережена браузером після отримання відповіді
response.cookies.set('theme', theme, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 30,
});
return response;
}Після запуску застосунку цей маршрут буде доступний за адресою /api/context.
Його можна перевірити запитом:
curl -i http://localhost:3000/api/contextПрапорець -i показує не тільки тіло, а й заголовки відповіді. У відповіді можна побачити x-request-id і set-cookie.
У Route Handler можна читати заголовки двома способами.
Через headers():
import { headers } from 'next/headers';
const requestHeaders = await headers();
const value = requestHeaders.get('x-custom-header');Або безпосередньо з об’єкта запиту:
export async function GET(request: Request) {
const value = request.headers.get('x-custom-header');
return Response.json({ value });
}Другий варіант зручний, коли обробник уже отримує параметр request. Для cookies можна використовувати cookies():
import { cookies } from 'next/headers';
export async function GET() {
const cookieStore = await cookies();
const sessionId = cookieStore.get('session_id')?.value;
return Response.json({
sessionId: sessionId ?? null,
});
}Server Action також може змінювати cookies, оскільки виконується на сервері та формує відповідь.
'use server';
import { cookies } from 'next/headers';
export async function setTheme(theme: string) {
const allowedThemes = ['light', 'dark'];
if (!allowedThemes.includes(theme)) {
throw new Error('Невідома тема');
}
const cookieStore = await cookies();
cookieStore.set('theme', theme, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 30,
});
}У цьому випадку cookie встановлюється під час виконання Server Action. Не слід викликати set() під час звичайного рендерингу Server Component.
Cookie не є безпечним сховищем лише тому, що вона встановлена сервером. Значення cookie може бути змінене клієнтом, якщо воно не захищене від підробки на рівні застосунку.
Для сесійних cookies варто дотримуватися таких правил:
не зберігати в cookie паролі та інші секрети у відкритому вигляді;
використовувати httpOnly: true для токенів ідентифікації сесії;
використовувати secure: true у production;
задавати обмеження sameSite;
вказувати конкретний path;
перевіряти значення cookie на сервері, а не довіряти йому автоматично.
Заголовки, отримані від клієнта, також не слід безумовно вважати достовірними. Наприклад, клієнт може сам надіслати заголовок x-user-id. Авторизацію потрібно визначати за перевіреною сесією або іншим надійним механізмом.
import { cookies } from 'next/headers';
export default async function Page() {
const cookieStore = await cookies();
// Так робити не можна під час рендерингу Server Component
cookieStore.set('theme', 'dark');
return <p>Сторінка</p>;
}Cookie потрібно встановлювати в Route Handler або Server Action.
awaitУ сучасному Next.js виклики headers() і cookies() потрібно очікувати:
const requestHeaders = await headers();
const cookieStore = await cookies();const response = NextResponse.json({ ok: true });
return response;
// Цей код уже не виконається
response.headers.set('x-status', 'ok');Усі зміни відповіді потрібно виконати до return.
secure: true під час локальної розробкиCookie з secure: true передається тільки через HTTPS. Під час розробки на http://localhost вона може не зберегтися.
Поширений варіант:
secure: process.env.NODE_ENV === 'production'pathЯкщо не вказати path, браузер може обмежити область дії cookie шляхом, з якого її було встановлено. Для cookie, яка має бути доступна всьому застосунку, зазвичай використовують:
path: '/'headers() читає заголовки поточного запиту на сервері.
cookies() читає cookies поточного запиту.
Заголовки відповіді встановлюються через response.headers.
Cookies встановлюються через response.cookies.set() або cookies().set().
Змінювати cookies можна в Route Handler і Server Action.
У Server Component cookies та заголовки можна читати, але cookies не можна змінювати під час рендерингу.
Для сесійних cookies зазвичай використовують httpOnly, secure, sameSite і path.
Заголовки та cookies можуть зробити сторінку динамічною, оскільки залежать від конкретного запиту.