Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vlopes's avatar

5.2 - CORS - Access-Control-Allow-Origin not in the headers

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

0 likes
6 replies
Sodium's avatar

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 ?

vlopes's avatar

Hello @Sodium

I think no, 5.5 still have the same piece of code

To add a route seems straightforward to me unless you want to have some control over authorization, and some packages will do it for you.

Sodium's avatar

Your solution works well enough for me, i'm just a little obsessive when i comes to best practices and clean code.

There HAS to be a better way to do this, i can't believe the developers behind Laravel would have left this kind of issue unanswered.

Sodium's avatar

I'm still getting errors with preflight requests. Sometimes it works, sometime doesn't. I always have a 200 response code for my OPTIONS requests but with an access-control-allow-headers error in the console.

The problem is i can't find a way to understand how to debug this since i can't output any message to know where the hell my request goes through.

I guess i'm gonna look into manually logging messages into files to get a closer look.

windsorperma's avatar

JSONP ( JSON with Padding ) is a method commonly used to bypass the cross-domain policies in web browsers. You're on domain example.com , and you want to make a request to domain example.net . To do so, you need to cross domain boundaries. JSONP is really a simple trick to overcome the XMLHttpRequest same domain policy. So, instead of using XMLHttpRequest we have to use < script > HTML tags, the ones you usually use to load JavaScript files , in order for JavaScript to get data from another domain.

Localhost

If you need to enable CORS on the server in case of localhost, you need to have the following on request header.

Access-Control-Allow-Origin: http://localhost:9999

More on CORS : http://net-informations.com/js/err/xml.htm

Please or to participate in this conversation.