Крайне распространённая проблема, которой забиты и тг-чаты, и 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-а. Например:
- для Nuxt proxy-module
- для React http-proxy-middleware
- для Nginx proxy_pass
- для Apache mod_proxy
# CORS и Server Errors
Обрати внимание, что если на сервере произойдёт критическая ошибка (500), то необходимые заголовки не будут отправлены и браузер запретит запрос, при этом узнать об ошибке на сервере можно будет только в логах или в системе мониторинга.