Пошук уроків, статей та іншого контенту
Обмежите дозволені джерела, методи й заголовки для кросдоменних запитів у NestJS.
CORS (Cross-Origin Resource Sharing) — механізм браузера, який визначає, чи може вебсторінка з одного джерела виконувати запити до сервера з іншого джерела.
Джерело складається з:
протоколу;
домену;
порту.
Наприклад, ці адреси є різними джерелами:
http://localhost:3000
http://localhost:5173
https://example.com
Якщо frontend працює на http://localhost:3000, а NestJS API — на http://localhost:3001, браузер застосує правила CORS.
CORS не є механізмом автентифікації. Він лише керує тим, які браузерні запити дозволені з конкретних джерел.
У NestJS CORS налаштовується у файлі main.ts за допомогою методу enableCors().
Найпростіший варіант дозволяє запити з усіх джерел:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3001);
}
bootstrap();Такий варіант зручний для швидкої локальної перевірки, але для реального застосунку краще явно вказувати дозволені джерела, методи та заголовки.
Для обмеження джерел передайте об’єкт налаштувань у enableCors():
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: [
'http://localhost:3000',
'https://app.example.com',
],
});
await app.listen(3001);
}
bootstrap();Тепер браузерні запити дозволені лише з таких джерел:
http://localhost:3000;
https://app.example.com.
Запит із http://localhost:5173 не матиме дозволених CORS-заголовків у відповіді, тому браузер заблокує доступ frontend до цієї відповіді.
Якщо API має використовувати лише один frontend, можна передати рядок:
app.enableCors({
origin: 'https://app.example.com',
});Порт є частиною джерела. Тому ці адреси потрібно вказувати окремо:
app.enableCors({
origin: [
'http://localhost:3000',
'http://localhost:5173',
],
});http://localhost:3000 і http://localhost:5173 — різні джерела.
Параметр methods визначає, які HTTP-методи дозволені для кросдоменних запитів:
app.enableCors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
});У цьому прикладі дозволені:
GET;
POST;
PUT;
PATCH;
DELETE.
Якщо frontend спробує виконати кросдоменний запит з іншим методом, браузер може заблокувати його.
Метод OPTIONS використовується браузером для попередньої перевірки запиту. CORS middleware обробляє такі перевірки автоматично, тому зазвичай не потрібно додавати окремий контролер для OPTIONS.
Параметр allowedHeaders визначає заголовки, які frontend може надсилати:
app.enableCors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
});У цьому прикладі дозволені:
Content-Type — наприклад, для JSON-запитів;
Authorization — наприклад, для Bearer-токена.
Frontend зможе виконати такий запит:
const response = await fetch('http://localhost:3001/users', {
method: 'GET',
headers: {
Authorization: 'Bearer example-token',
},
});
const users = await response.json();
console.log(users);Якщо запит містить заголовок, якого немає в allowedHeaders, браузер спочатку виконає preflight-запит. Якщо сервер не дозволить цей заголовок, основний запит не буде надіслано.
Ось приклад типової конфігурації для frontend і API, які працюють на різних портах:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
// Дозволені frontend-застосунки
origin: [
'http://localhost:3000',
'https://app.example.com',
],
// Дозволені HTTP-методи
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
// Дозволені заголовки запиту
allowedHeaders: ['Content-Type', 'Authorization'],
// Дозволяє надсилати cookies та інші облікові дані
credentials: true,
});
await app.listen(3001);
}
bootstrap();Цей файл можна використовувати як src/main.ts у стандартному NestJS-проєкті. API слухатиме порт 3001, а запити з http://localhost:3000 і https://app.example.com будуть дозволені за вказаними правилами.
credentialsЯкщо frontend надсилає cookies або інші облікові дані, потрібно:
увімкнути credentials: true на сервері;
вказати конкретні дозволені джерела;
додати credentials: 'include' у frontend-запиті.
Конфігурація NestJS:
app.enableCors({
origin: 'http://localhost:3000',
credentials: true,
});Запит із frontend:
const response = await fetch('http://localhost:3001/profile', {
credentials: 'include',
});
const profile = await response.json();
console.log(profile);Не можна поєднувати credentials: true з універсальним джерелом:
app.enableCors({
origin: '*',
credentials: true,
});Браузер не дозволяє використовувати Access-Control-Allow-Origin: * разом із credentials. Якщо потрібні cookies, завжди вказуйте конкретне джерело.
CORS контролює поведінку браузера. Він не блокує сам HTTP-запит на рівні мережі.
Наприклад, інший сервер або утиліта командного рядка можуть звернутися до API незалежно від CORS. Тому CORS не замінює:
автентифікацію;
авторизацію;
перевірку токенів;
обмеження доступу до API.
CORS потрібен для контролю кросдоменних запитів, які виконуються у браузері.
Для початку розробки можна використовувати:
app.enableCors({
origin: 'http://localhost:3000',
});Для більш повної конфігурації:
app.enableCors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
});Важливо, щоб значення origin точно збігалося з адресою frontend:
http і https відрізняються;
різні порти відрізняються;
домен localhost відрізняється від 127.0.0.1;
зайвий слеш у кінці адреси може спричинити невідповідність.
Frontend працює на http://localhost:5173, але в CORS вказано лише http://localhost:3000.
Виправлення:
app.enableCors({
origin: 'http://localhost:5173',
});* разом із credentialsНеправильно:
app.enableCors({
origin: '*',
credentials: true,
});Правильно:
app.enableCors({
origin: 'http://localhost:3000',
credentials: true,
});AuthorizationЯкщо frontend надсилає Bearer-токен, але Authorization відсутній у allowedHeaders, preflight-перевірка може завершитися помилкою.
app.enableCors({
origin: 'http://localhost:3000',
allowedHeaders: ['Content-Type', 'Authorization'],
});enableCors() потрібно викликати для об’єкта застосунку до запуску сервера:
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: 'http://localhost:3000',
});
await app.listen(3001);Postman та інші серверні HTTP-клієнти не застосовують браузерні правила CORS. Тому запит у Postman може працювати, а запит із frontend у браузері — ні.
Перевіряйте CORS саме з браузерного застосунку та переглядайте вкладку Network у DevTools.
CORS визначає, з яких джерел браузер може звертатися до NestJS API.
CORS налаштовується через app.enableCors().
origin обмежує дозволені джерела.
methods обмежує HTTP-методи.
allowedHeaders обмежує заголовки запитів.
Для cookies потрібно використовувати credentials: true і конкретне джерело.
CORS не замінює автентифікацію та авторизацію.
Порт, протокол і домен входять до поняття джерела та мають точно збігатися.