Blocked by CORS policy

Февраль, 2024

Крайне распространённая проблема, которой забиты и тг-чаты, и stack overflow, и qna хабр... Активно набирает популярность с ростом популярности SPA и раздельных доменов для frontend и backend приложений.

# Проблема

AJAX-запрос на "api" не работает в браузере, но работает в postman. Ошибка в консоли браузера:

Access to XMLHttpRequest at 'http://localhost:8000' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

# Теория

CORS (Cross-Origin Resource Sharing) простыми словами - механизм безопасности, который ограничивает кросс-доменные запросы согласно политике «Same-origin policy» .

Статьи по ссылкам обязательны к осмысленному прочтению всем web-разработчикам.

# Localhost

«У меня везде localhost, почему не работает?»

Адреса считаются разными, если у них отличается хотя бы что-то одно:

  • домен
  • порт
  • протокол

# Postman

«А почему у меня в postman работает?»

В postman отключены CORS, он не следует заголовкам и не ограничивает свои запросы, т.к. это инструмент разработки, а не браузер. Тоже самое касается любого клиента, который не следует SOP.

# Решение

Необходимо настроить CORS со стороны backend-приложения. Не надо пытаться отключить или "обойти" проблему, даже для локальной разработки.

Начиная с 7 версии, Laravel поддерживает CORS из коробки. Настройки в config/cors.php

По умолчанию разрешены любые запросы с любых источников на /api/*, например, GET http://localhost:8000/api/users

Проблемы при локальной разработке возникают чаще всего из-за того, что запросы приходят не на /api, поэтому необходимо или отправлять запросы на /api, или добавить необходимые пути в конфиг.

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

# Middleware

Так же проблема может быть из-за не подключенного middleware в App\Http\Kernel

Проверь свойство $middleware, в нем должен быть указан один из следующих классов:

  • \Illuminate\Http\Middleware\HandleCors для 9.2+
  • \Fruitcake\Cors\HandleCors для 8.x и ниже, при использовании пакета fruitcake/laravel-cors

# Старые версии

Для более ранних версий (8.x и ниже) необходим пакет fruitcake/laravel-cors (в 7.x и 8.x он установлен по умолчанию) или собственная реализация.

Если фреймворк обновлялся до версии 9.2 или выше, то пакет fruitcake/laravel-cors необходимо удалить, а middleware заменить на \Illuminate\Http\Middleware\HandleCors, подробнее смотри в PR .

# Решение с proxy

Альтернативный вариант - это отправлять запросы на тот же адрес, с которого работает frontend (внезапно), как это и происходит в монолитных приложениях.

Если frontend и backend расположены на разных доменах, поддоменах, ip-адресах или портах, то можно сделать проксирование запросов, т.е. отправлять с клиента запрос на текущий адрес, а далее он будет перенаправлен сервером на реальный адрес backend-а. Например:

# CORS и Server Errors

Обрати внимание, что если на сервере произойдёт критическая ошибка (500), то необходимые заголовки не будут отправлены и браузер запретит запрос, при этом узнать об ошибке на сервере можно будет только в логах или в системе мониторинга.