CORS объяснено Как отладить ошибки перекрестного происхождения без того, чтобы потерять разум

Опубликовано
CORS объяснено: как отладить ошибки перекрестного происхождения без того, чтобы потерять разум 1
Реклама · УДАЛИТЬ?

Вы сделали запрос fetch. Вкладка сети показывает, что он был инициирован. Но в консоли появляется стена красного цвета: Доступ к fetch по адресу 'https://api.example.com' из источника 'https://yourapp.com' был заблокирован политикой CORS.

Перед тем как переходить к объяснениям — вот самый быстрый путь диагностики. Откройте DevTools → вкладку сети → найдите неудачный запрос → посмотрите на Заголовки ответа. Если вы не видите Access-Control-Allow-Origin, ваш сервер не отправляет заголовки CORS. Это и есть решение. Остальная часть этой статьи объясняет, что именно нужно отправлять и почему.

Что такое CORS на самом деле

CORS — Cross-Origin Resource Sharing — обеспечивается браузером, а не сервером. Ваш API не блокирует по умолчанию запросы из других источников. Браузер делает это от имени пользователей, чтобы предотвратить, чтобы скрипты на evil.com не могли безвозвратно читать данные из вашего банковского аккаунта.

Браузер проверяет: «Разрешает ли ответ от этого API тому источнику, чтобы он мог его читать?» Если нет, он блокирует ответ — даже если сервер уже обработал запрос и вернул статус 200. Сервер никогда не узнает, почему клиент отклонил его.

Это важно при отладке: ошибка всегда возникает на клиентской стороне. Сервер должен сообщить браузеру: «Да, этот источник разрешён». Именно это делают заголовки ответа CORS.

Простые запросы и запросы предварительной проверки

Не все запросы из разных источников ведут себя одинаково. Браузер различает два типа.

Простые запросы — это запросы GET или POST с простыми телами (текстом или форматированным телом) и небольшим набором разрешённых заголовков. Браузер отправляет их напрямую и проверяет ответ на Access-Control-Allow-Origin.

Предварительные запросы происходят первыми, когда ваш запрос не соответствует этим условиям — например, когда вы отправляете PUT или DELETE, включаете пользовательский заголовок, например Authorization или Content-Type: application/json, или отправляете учетные данные. Браузер запускает автоматический OPTIONS запрос к тому же URL перед вашим реальным запросом. Если сервер не отвечает на этот OPTIONS запрос с правильными заголовками CORS, ваш реальный запрос никогда не покидает браузер.

Если вы видите OPTIONS запросы в вкладке сети, которые возвращают 404 или 405, это объясняет, почему ваши запросы не срабатывают. Ваш сервер должен обрабатывать OPTIONS для каждой маршруты, которая получает трафик из разных источников.

Заголовки, которые важны

Правильное применение заголовков CORS означает понимание того, что каждый заголовок ответа на самом деле контролирует:

  • Access-Control-Allow-Origin — какие источники могут читать ответ. Либо конкретный источник (https://yourapp.com), либо * для любого источника.
  • Access-Control-Allow-Methods — какие HTTP-методы разрешены (например, GET, POST, PUT, DELETE, OPTIONS).
  • Access-Control-Allow-Headers — какие заголовки запроса разрешены браузеру (например, Authorization, Content-Type).
  • Access-Control-Allow-Credentials — может ли передаваться в запросе куки и заголовки аутентификации. Должно быть true явно указано.
  • Access-Control-Max-Age — на сколько секунд браузер должен кэшировать ответ предварительной проверки.

Ловушка с звёздочкой

Использование Access-Control-Allow-Origin: * — самый быстрый способ открыть ваш API — но оно нарушается, как только вы добавляете учетные данные. Когда Access-Control-Allow-Credentials: true требуется, звёздочка отклоняется браузером. Вам нужно указать точный источник:

# This will fail with credentials:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

# This works:
Access-Control-Allow-Origin: https://yourapp.com
Access-Control-Allow-Credentials: true

Если у вас есть несколько разрешённых источников, прочитайте заголовок запроса Origin и отразите его условно — не объединяйте их.

Частые ошибки CORS и то, что они означают

Обычно сообщение об ошибке от браузера указывает на то, что отсутствует именно то, что нужно. Вот краткая справка:

Сообщение об ошибкеЧто это означаетКак исправить
Отсутствует заголовок 'Access-Control-Allow-Origin'Сервер не отправил никаких заголовков CORS вообщеДобавить Access-Control-Allow-Origin в ответ
Значение заголовка 'Access-Control-Allow-Origin' … не соответствует указанному источникуНесоответствие источника — сервер вернул неправильный или звёздочку с учетными даннымиОтразите заголовок запроса Origin условно; удалите звёздочку при использовании учетных данных
Метод PUT не разрешён в Access-Control-Allow-MethodsHTTP-метод не указан в заголовке разрешённых методовДобавьте недостающий метод в Access-Control-Allow-Methods
Заголовок запроса 'Authorization' не разрешён в Access-Control-Allow-HeadersПользовательский заголовок отсутствует в списке разрешённыхДобавьте заголовок в Access-Control-Allow-Headers
Ответ на предварительный запрос не проходит проверку контроля доступаЗапрос OPTIONS вернул неправильный статус или отсутствует заголовкиОбработайте запрос OPTIONS явно; верните 200/204 с правильными заголовками
Учетные данные не поддерживаются, если заголовок CORS 'Access-Control-Allow-Origin' равен '*'Использована звёздочка при режиме учетных данныхЗамените * на явный источник; добавьте Access-Control-Allow-Credentials: true

Как настроить CORS в Express, FastAPI и Nginx

Express (Node.js)

const cors = require('cors');

app.use(cors({
  origin: 'https://yourapp.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
}));

// Handle preflight for all routes
app.options('*', cors());

FastAPI (Python)

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://yourapp.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Nginx

location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://yourapp.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Max-Age' 86400;
        add_header 'Content-Length' 0;
        return 204;
    }

    add_header 'Access-Control-Allow-Origin' 'https://yourapp.com';
    add_header 'Access-Control-Allow-Credentials' 'true';

    proxy_pass http://backend;
}

Примечание по шаблону Nginx: вам нужно add_header в обоих блоках OPTIONS и основного блока. Заголовки, установленные внутри if блоков, не передаются в основной блок.

Ваш список проверки при отладке

Когда вы сталкиваетесь с ошибкой CORS, выполните это по порядку:

  1. Прочитайте полное сообщение об ошибке — оно точно указывает на то, какой заголовок или значение неверно указаны.
  2. Проверьте вкладку сети — посмотрите на реальные заголовки ответа, а не на то, что вы думаете, что настроили.
  3. Проверьте наличие запроса OPTIONS — если он не срабатывает или отсутствует, ваш сервер не обрабатывает предварительную проверку.
  4. Проверьте, чтобы источник соответствовал точно — заканчивающиеся слэшами, HTTP против HTTPS и номера портов имеют значение.
  5. Удалите звёздочку, если вы используете учетные данные — они взаимно исключают друг друга.
  6. Проверьте, не удаляются ли заголовки в прокси — Nginx и CDN иногда удаляют или переопределяют заголовки CORS.

Одна ловушка, легко упускаемая: если ваш API находится за обратным прокси или CDN, это слой может добавить свой собственный заголовок Access-Control-Allow-Origin , который конфликтует с тем, что ваш сервер возвращает. Когда в одном ответе появляются два таких заголовка, браузер отклоняет весь ответ. Всегда проверяйте исходные заголовки ответа на уровне сети — не только то, что ваше приложение генерирует.

Еще один крайний случай: некоторые фреймворки присоединяют заголовки CORS только к маршрутам, соответствующим зарегистрированному обработчику. Если вы сталкиваетесь с ошибкой 404 или несуществующим маршрутом, заголовки CORS могут не быть применены, и вы увидите ошибку «нет заголовка», даже если настройка выглядит корректно. Сначала протестируйте с действительным конечным пунктом.

Если вам нужно быстро сгенерировать точные заголовки CORS, которые должен возвращать ваш сервер, то IO Tools CORS Headers Builder позволяет настроить источник, методы и учетные данные и выводит правильный блок заголовков, готовый к вставке в настройку сервера.

Примечание по безопасности

CORS не является механизмом безопасности API. Установка Access-Control-Allow-Origin: * не делает ваш API публичным в каком-либо смысле, связанным с угрозой безопасности — curl, Postman и серверные вызовы никогда не подвергаются ограничениям CORS. Только JavaScript в браузере подвергается этим ограничениям. Если ваш API требует аутентификации, обеспечьте это на уровне API с помощью токенов или сессий. Заголовки CORS только сообщают браузеру, какие источники разрешены для чтения ответов от JavaScript. Они не связаны с реальным контролем доступа.

С учетом этого контекста вы можете принимать обоснованные решения относительно настройки CORS, вместо того чтобы либо слишком сильно закрывать всё, из-за неправильного страха перед безопасностью, либо слишком широко открывать всё без понимания торговых компромиссов.

Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?