Laravel 10. Как решить проблему с CORS при получении фотографий?
Laravel работает как API сервис для фронта, фронт на react.
Обычные API ссылки вызываются без проблем: https://api.dev.site.ru/api/products
Фотки находятся по адресу https://api.dev.site.ru/images/tickets/cat.jpg
Запрос идет с адреса: https://develop.app.site.ru/
Доступ к фотке закрыт по Bearer token.
Если я локально тестирую где у меня и бек и фронт на одном домене test.ru, то там все хорошо. Фотка отображается у нее код ответа 200.
Если я в postman открываю фотку с удаленного сайта https://api.dev.site.ru/images/tickets/cat.jpg, то она открывается но у него код ответа почему то 404, у локальной фотки код ответа 200. И локально и удалено один и тот же сайт с одинаковыми настройками.
Убрав проверку авторизации с фотки, она открылась на сайте, но ошибка CORS осталась и по прямой ссылке в браузере она открывается с кодом 404
Файл config/cors.php:
<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | */ 'paths' => ['api/*', 'sanctum/csrf-cookie', 'images/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ]; |
<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | */ 'paths' => ['api/*', 'sanctum/csrf-cookie', 'images/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ];
Файл app/Provited/RouteServiceProvider.php:
<?php namespace AppProviders; use IlluminateCacheRateLimitingLimit; use IlluminateFoundationSupportProvidersRouteServiceProvider as ServiceProvider; use IlluminateHttpRequest; use IlluminateSupportFacadesRateLimiter; use IlluminateSupportFacadesRoute; class RouteServiceProvider extends ServiceProvider { /** * The path to the "home" route for your application. * * This is used by Laravel authentication to redirect users after login. * * @var string */ public const HOME = '/home'; /** * The controller namespace for the application. * * When present, controller route declarations will automatically be prefixed with this namespace. * * @var string|null */ // protected $namespace = 'App\Http\Controllers'; /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot(): void { $this->configureRateLimiting(); $this->routes(function () { Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); Route::prefix('images') ->middleware('cors') ->namespace($this->namespace) ->group(base_path('routes/images.php')); Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); }); } /** * Configure the rate limiters for the application. * * @return void */ protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); RateLimiter::for('images', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); } } |
<?php namespace AppProviders; use IlluminateCacheRateLimitingLimit; use IlluminateFoundationSupportProvidersRouteServiceProvider as ServiceProvider; use IlluminateHttpRequest; use IlluminateSupportFacadesRateLimiter; use IlluminateSupportFacadesRoute; class RouteServiceProvider extends ServiceProvider { /** * The path to the "home" route for your application. * * This is used by Laravel authentication to redirect users after login. * * @var string */ public const HOME = '/home'; /** * The controller namespace for the application. * * When present, controller route declarations will automatically be prefixed with this namespace. * * @var string|null */ // protected $namespace = 'App\Http\Controllers'; /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot(): void { $this->configureRateLimiting(); $this->routes(function () { Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); Route::prefix('images') ->middleware('cors') ->namespace($this->namespace) ->group(base_path('routes/images.php')); Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); }); } /** * Configure the rate limiters for the application. * * @return void */ protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); RateLimiter::for('images', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); } }
Файл config/auth.php:
<?php return [ /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], /* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider. | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session" | */ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'images' => [ 'driver' => 'passport', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ] ], /* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => AppModelsUser::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], /* |-------------------------------------------------------------------------- | Password Confirmation Timeout |-------------------------------------------------------------------------- | | Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ 'password_timeout' => 10800, ]; |
<?php return [ /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], /* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider. | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session" | */ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'images' => [ 'driver' => 'passport', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ] ], /* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => AppModelsUser::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], /* |-------------------------------------------------------------------------- | Password Confirmation Timeout |-------------------------------------------------------------------------- | | Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ 'password_timeout' => 10800, ];
Файл routes/images.php:
<?php use AppHttpControllersImageController; use IlluminateSupportFacadesRoute; Route::middleware('auth:images')->group(function () { Route::get('tickets/{filename}', [ImageController::class, 'showTicketImage']); }); |
<?php use AppHttpControllersImageController; use IlluminateSupportFacadesRoute; Route::middleware('auth:images')->group(function () { Route::get('tickets/{filename}', [ImageController::class, 'showTicketImage']); });
Файл app/Http/Controllers/ImageController.php:
<?php namespace AppHttpControllers; use AppModulesCommon; use InterventionImageFacadesImage; use IlluminateHttpRequest; class ImageController extends Controller { /** * Получение фотки тикета * * @param Request $request * @param string $fileName * @return mixed */ public function showTicketImage(Request $request, string $fileName): mixed { $path = storage_path(Common::TICKET_PATH . $fileName); if (!file_exists($path)) return response('', 404); return Image::make($path)->response(); } } |
<?php namespace AppHttpControllers; use AppModulesCommon; use InterventionImageFacadesImage; use IlluminateHttpRequest; class ImageController extends Controller { /** * Получение фотки тикета * * @param Request $request * @param string $fileName * @return mixed */ public function showTicketImage(Request $request, string $fileName): mixed { $path = storage_path(Common::TICKET_PATH . $fileName); if (!file_exists($path)) return response('', 404); return Image::make($path)->response(); } }
Файл nginx стандартный который при установке nginx последней версии там добавляется.
Дополнительно:
1) Где текст CORS ошибки?
2) Почему в роутах /images/tickets/{filename} а в примерах https://api.dev.site.ru/images/cat.jpg?
images/tickets != images Этот урл вообще не должен обрабатываться твои контроллером (ImageController@showTicketImage).
- это я не правильно ссылку указал, поправил. Вообщем /images/tickets/{fileName}
- Cross-Origin Resource Sharing error: PreflightMissing Allow OriginHeader такая ошибка
-
Если в хроме в нетворк выбрать ALL то вот три строчки таких на фотку получается.
- Убрав проверку авторизации с фотки, она открылась на сайте, но ошибка CORS осталась и по прямой ссылке в браузере она открывается с кодом 404
- Для этого роута supports_credentials должно быть true, так как там есть Bearer token
- Kerm, убрал только со стороны бека? А фронт по прежнему отправляет Authorization заголовок?
- YepBro, да
- YepBro, дык api методы они все с токенами в config/cors.php: там одни настройки для api/* и images/*
- Kerm, ну тогда supports_credentials должно быть true в config/cors.php для этого роута.
- YepBro, знаешь чего еще нашел, метод OPTIONS при запросе фото возвращает:
<html> <head> <title>405 Not Allowed</title> </head> <body> <center> <h1>405 Not Allowed</h1> </center> <hr> <center>nginx</center> </body> </html>
<html> <head> <title>405 Not Allowed</title> </head> <body> <center> <h1>405 Not Allowed</h1> </center> <hr> <center>nginx</center> </body> </html>
тогда как /api нормально работает с options
- Kerm, Это ответ от nginx, а не от твоего бекенда. Стоит посмотреть конфиг nginx, может там картинки отдельным локейшеном обрабатываются. И еще, нет ли на диске этих картинок в public/images/tickets/* ?
- YepBro, картинки хранятся по адресу storage/app/public/tickets-images
- YepBro, конфиг nginx стандартный:
- Kerm, попробуй закоментировать строку expires 5d;
- YepBro, спасибо! дело оказалось в кеше картинок, убрал и все заработало как надо.
- Kerm, nginx? или в laravel был свой реализован?
- YepBro, nginx, там для картинок стояло expires 5d; конфиг выше можно посмотреть
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос



Для решения проблемы с CORS (Cross-Origin Resource Sharing) при получении фотографий в Laravel 10, необходимо выполнить несколько шагов.
1. Установите пакет barryvdh/laravel-cors с помощью Composer. Вы можете сделать это, запустив следующую команду в корневой директории вашего проекта:
composer require barryvdh/laravel-cors
2. После установки пакета, опубликуйте его конфигурационный файл, запустив команду:
php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"
3. Теперь вам нужно настроить CORS в файле config/cors.php. Вы можете разрешить доступ к вашему приложению с определенных доменов, настроив массив allowed_origins в этом файле. Например:
'allowed_origins' => ['http://example.com', 'https://example.com'],
4. Вы также можете настроить другие параметры CORS, такие как allowed_methods, allowed_headers и т.д. в файле config/cors.php.
5. Не забудьте добавить промежуточное ПО Cors в ваше приложение. Для этого откройте файл app/Http/Kernel.php и добавьте Cors в массив $middleware:
protected $middleware = [ // другие промежуточные ПО \Barryvdh\Cors\HandleCors::class, ];
После выполнения этих шагов, проблема с CORS при получении фотографий в Laravel 10 должна быть успешно решена. Ваши запросы к фотографиям теперь должны проходить без проблем с CORS.