#Что означает ошибка
Access to fetch at ... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
Access to fetch at ... from origin ... has been blocked by CORS policy — браузер запретил читать ответ, потому что сервер не разрешил ваш источник (origin). Запрос мог дойти до сервера и выполниться, но результат скрыт. Ошибка возникает только в браузере, не в curl и Postman.
CORS настраивается на сервере, а не в клиентском коде.
Разберём типовые конфигурации и режим разработки.
Браузер блокирует запрос к другому источнику, если сервер не разрешил его заголовками CORS. Исправлять нужно на стороне сервера, а не в клиентском коде.
add_header Access-Control-Allow-Origin "https://example.com" always;#Причины и решения
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Нет Access-Control-Allow-Origin | Ответ API не содержит подходящий Access-Control-Allow-Origin | Разрешите точный origin на сервере API; посетителю чужого сайта это недоступно. |
| Preflight-запрос не обработан | Сетевой OPTIONS возвращает 401/403 или не содержит нужных разрешений | На сервере настройте маршрут preflight и разрешённые методы/заголовки. |
| Неверный источник | Различаются протокол, домен или порт origin и разрешённый сервером адрес | Сверьте полную строку Origin, а не только имя сайта. |
| Передача cookie | Для cookies указан credentials: include, но сервер разрешает * либо не разрешает credentials | Разрешите конкретный origin и Allow-Credentials true; проверьте политику cookies. |
#Пошаговое решение
- Откройте DevTools → NetworkЭто статья для разработчика своего сайта или API, не для посетителя чужой страницы. В Chrome/Edge нажмите F12 (или Ctrl+Shift+I) → вкладка «Сеть» (Network), включите «Сохранить журнал» (Preserve log) при переходах и воспроизведите ошибку. Щёлкните красный запрос к API и запишите адрес запроса, метод, HTTP-статус и значение
Originв «Заголовках». Если запрос не появился, проверьте вкладку Console и блокировку расширениями/сетью. Не пытайтесь устранять CORS отключением безопасности браузера. - Посмотрите ответ на OPTIONSВо вкладке Network найдите запрос
OPTIONSнепосредственно перед неудачным запросом (он появляется не всегда: для простого запроса предварительной проверки может не быть). Откройте его «Заголовки» → Response Headers: сопоставьтеAccess-Control-Allow-Originс исходным адресом страницы, аAccess-Control-Allow-MethodsиAccess-Control-Allow-Headers— с методом и запрошенными заголовками. Код 200/204 сам по себе не гарантирует успех CORS. При ошибке 401/403 на OPTIONS проверяйте маршрутизацию, авторизацию и прокси на сервере. - Добавьте Access-Control-Allow-Origin на сервереЕсли вы управляете API, настройте CORS именно в обработчике ответов API или на обратном прокси по документации вашего стека. Укажите точный origin полностью (
https://site.example, с учётом схемы и порта); в простом сценарии сервер должен отправлятьAccess-Control-Allow-Origin: https://site.example. Для предварительного запроса дополнительно разрешите реально используемый метод и заголовки, и обеспечьте корректный ответ на OPTIONS без обязательной авторизации пользователя. Не добавляйте этот заголовок одновременно из PHP, Nginx и приложения: дублирующиеся значения часто ломают проверку. - Для cookie включите credentialsТолько если запрос требует cookies или другого учётного состояния, у клиента
fetchзадайтеcredentials: "include"и обеспечьте на сервереAccess-Control-Allow-Credentials: trueвместе с конкретным разрешённым origin (не*). Проверьте также атрибуты cookieSameSite,Secureи политику браузера: CORS не отменяет блокировку сторонних cookies. Без учётных данных этот шаг пропустите; не включайте поддержку credentials «на всякий случай». - В разработке используйте прокси dev-сервераЕсли API сторонний и не разрешает ваш origin, на клиенте исправить это нельзя. Для локальной разработки настройте прокси в Vite
server.proxyлибо в используемом сервере разработки, чтобы запрос шёл к тому же origin; для рабочего сайта выполняйте запрос к стороннему API на своём сервере при соблюдении его условий использования. Повторите запрос в чистой вкладке Network и убедитесь, что браузер реально получил JSON/другой ожидаемый ответ. Если CORS остаётся, передайте администратору точный origin, метод, заголовки и ответ OPTIONS.
#Как это работает
Для «простых» запросов браузер добавляет заголовок Origin, а сервер должен ответить Access-Control-Allow-Origin. Для запросов с нестандартными заголовками, Content-Type: application/json или методами PUT/DELETE браузер сначала отправляет OPTIONS (preflight), и сервер обязан ответить заголовками Access-Control-Allow-Methods и Access-Control-Allow-Headers. Проверьте в DevTools → Network ответ на OPTIONS.
#Настройка серверов
Express: npm i cors, app.use(cors({ origin: "https://site.ru" })). nginx: add_header Access-Control-Allow-Origin "https://site.ru" always; плюс обработка OPTIONS с кодом 204. PHP: header("Access-Control-Allow-Origin: https://site.ru");. Для запросов с cookie: credentials: "include" на клиенте и Access-Control-Allow-Credentials: true с конкретным origin (звёздочка не работает). Перечислите заголовки через Access-Control-Allow-Headers.
#Разработка
Используйте прокси dev-сервера: Vite — server.proxy, webpack — devServer.proxy, Next.js — rewrites. Так запросы идут с того же origin. Не используйте расширения «отключить CORS» кроме тестов. Для сторонних API без CORS делайте запрос с вашего сервера. Проверьте, что вы обращаетесь по https, а не http, и что редирект не теряет заголовки (CORS не работает с редиректами при preflight).
# Только диагностический запрос: подставьте фактические адреса и методcurl -i -X OPTIONS https://api.example.com/items -H "Origin: https://site.example" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: content-type"