ЯдроКодаподготовка к экзаменам
Учебная платформа

Загружаем материалы

Подготавливаем материалы и навигацию по разделу.

CORS error: как найти причину и исправить на сервере

Автор: · Обновлено

CORS error появляется, когда браузерный JavaScript обращается к другому origin, а ответ сервера не разрешает странице прочитать результат по правилам Fetch. Исправление находится на сервере или reverse proxy: клиент не может выдать себе разрешение заголовком запроса.

Что считается другим origin

Origin состоит из схемы, имени хоста и порта. https://app.example.ru и https://api.example.ru имеют разные хосты; http://localhost:5173 и http://localhost:3000 — разные порты; HTTP и HTTPS — разные схемы. Путь после хоста на origin не влияет.

CORS — браузерный механизм доступа к ответу. Запрос через curl, backend-to-backend HTTP или Postman не доказывает, что браузер сможет прочитать тот же ответ. И наоборот, CORS не является аутентификацией и не запрещает серверу получить запрос: API всё равно обязан проверять пользователя, права и входные данные.

Как проходит простой CORS-запрос

Браузер добавляет Origin, например Origin: https://app.example.ru. Сервер должен вернуть подходящий Access-Control-Allow-Origin. Для публичного ответа без credentials допустим *; для cookies или другого credentialed режима нужен конкретный origin, а не wildcard.

Проверить заголовок можно независимо от frontend-кода:

curl -i 'https://api.example.ru/health'   -H 'Origin: https://app.example.ru'

Ищите Access-Control-Allow-Origin: https://app.example.ru в фактическом HTTP-ответе, включая ответы 401, 403 и 500. Если успешный ответ содержит заголовок, а ошибка — нет, браузер покажет CORS-проблему и скроет полезное тело именно в аварийной ветке.

Зачем браузер отправляет preflight OPTIONS

Для запроса, который не относится к CORS-safelisted, браузер сначала делает preflight. Например, PATCH, заголовок Authorization или несейфлистовый Content-Type обычно требуют OPTIONS. Предварительный запрос сообщает желаемый метод и заголовки, а сервер отвечает разрешёнными значениями.

curl -i -X OPTIONS 'https://api.example.ru/lessons/42'   -H 'Origin: https://app.example.ru'   -H 'Access-Control-Request-Method: PATCH'   -H 'Access-Control-Request-Headers: authorization,content-type'

Успешный preflight должен согласовать origin, метод и запрошенные заголовки. Его ответ не обязан содержать бизнес-данные. Если proxy не пропускает OPTIONS, перенаправляет его на страницу входа или требует bearer token до CORS-обработчика, основной PATCH вообще не будет отправлен браузером.

Корректная настройка CORS в NestJS

Настройте CORS один раз на HTTP-границе приложения и перечислите доверенные frontend-origin. Следующий фрагмент подходит для main.ts NestJS-проекта и разрешает production UI и локальную разработку.

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap(): Promise<void> {
  const app = await NestFactory.create(AppModule);

  app.enableCors({
    origin: ["https://app.example.ru", "http://localhost:5173"],
    methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
    allowedHeaders: ["Content-Type", "Authorization"],
    credentials: true,
    maxAge: 600,
  });

  await app.listen(3000);
}

void bootstrap();

Список должен поступать из проверенной конфигурации окружения, если домены различаются между стендами. Не отражайте любой входной Origin без allowlist: это фактически превращает ограничение в разрешение для произвольного сайта. Если cookies не используются, не включайте credentials автоматически.

Диагностика по слоям

Начните во вкладке Network браузера, а не только с текста Console. Найдите основной запрос и возможный OPTIONS, запишите request URL, Origin, status, redirect и все Access-Control-* заголовки. Затем повторите preflight через curl, чтобы отделить серверную конфигурацию от поведения приложения.

Проверяйте слои по порядку: backend, ingress или Nginx, CDN, затем браузер. CORS-заголовок должен формироваться в одном ответственном месте. Если backend и proxy оба добавляют Access-Control-Allow-Origin, дублированные значения могут сделать ответ недействительным. Redirect на другой origin также требует анализа уже конечного ответа.

Типовые причины:

Почему клиентские обходы не исправляют CORS

Добавление Access-Control-Allow-Origin в fetch создаёт обычный пользовательский request header и не разрешает доступ: разрешающий заголовок должен прийти в ответе. Режим mode: "no-cors" возвращает opaque response, содержимое и статус которого JavaScript прочитать не может; это не решение для JSON API.

Расширение, отключающее web security, годится только для изолированного эксперимента и скрывает дефект от разработчика. JSONP и публичный proxy меняют архитектуру и поверхность риска. Для своего API правильный путь — согласовать точный origin на сервере и проверить preflight.

Практика: воспроизведите и устраните отказ

Запустите frontend на http://localhost:5173, а NestJS API — на http://localhost:3000. Сделайте PATCH с Content-Type: application/json и Authorization, сначала не включая CORS. Зафиксируйте в Network отдельный OPTIONS и причину отказа. Затем добавьте точный localhost-origin, методы и заголовки в enableCors, повторите запрос и сравните оба ответа.

После успеха намеренно удалите PATCH из методов, затем Authorization из заголовков. Для каждого случая предскажите, какой заголовок ответа станет причиной отказа. Наконец, повторите запрос через curl с Origin и убедитесь, что проверяется именно серверный контракт.

Что важно запомнить

Источники