Hello,
Having the same issue and i now understand while OPTIONS requests never passed through my middlewares.
Your solution works but there has to be a cleaner way to do this ?
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
Hello,
Lots of people are complaining about the following error when trying to perform a cross-origin request:
XMLHttpRequest cannot load http://localhost:8000/api/v1/ping. No 'Access-Control-Allow-Origin' header is present on the requested resource.
Modern browsers, as specified by w3.org ( https://www.w3.org/TR/cors/ ) perform a preflight request with method OPTIONS to the server on the desired URI. If the server does not answer with the following example headers:
Access-Control-Allow-Origin: http://vlopes.com.br
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Allow-Headers: X-Test-Anything, X-Yet-Another-Test, Content-Type
Access-Control-Max-Age: 99500
The process is classified as untrusted and therefore denied.
In order to fix this, I’ve created a middleware:
<?php namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With')
->header('Access-Control-Allow-Origin', '*');
}
}
Listed it on app/Http/Kernel.php
protected $routeMiddleware = [
'cors' => \App\Http\Middleware\CorsMiddleware::class,
(...)
];
And protected my route with the middleware
Route::group(['middleware' => ['cors']], function() { (...) });
But, still, the OPTIONS preflight request didnt aswer with the desired headers. Digging into the problem, I stated that the following code from /vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php
protected function getRouteForMethods($request, array $methods)
{
if ($request->method() == 'OPTIONS') {
return (new Route('OPTIONS', $request->path(), function () use ($methods) {
return new Response('', 200, ['Allow' => implode(',', $methods)]);
}))->bind($request);
}
$this->methodNotAllowed($methods);
}
always returns a 200 response without the middleware headers. So, to fix this, I added a route to overwrite OPTIONS:
Route::options('{all}', function () {
return response('ok', 200)
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With')
->header('Access-Control-Allow-Origin', '*');
})->where('all', '.*');
This worked, but I’m quite sure that this is not the right way. Should $methods from getRouteForMethods be injected with something?
Thanks, Victor Lopes
Please or to participate in this conversation.