16

I recently tries enabling CORS in Laravel 5.4 but unfortunately it doesn't want to work. I have included the code and the error that it's giving me below. Can anyone help finding out why it isn't working? I have passed the required headers.

I have renamed my domain to domain.uk just for example purposes and I don't wan't to expose the domain of my site just yet as its under development.

Routes (Made the one route ::any for testing purposes while developing, usually on production it would be post):

Route::group(['domain' => 'api.domain.uk', 'namespace' => 'Api'], function() {
    Route::group(['middleware' => ['cors'], 'prefix' => 'call'], function() {
        Route::get('/rooms/{id}/get-locked-status', 'ApiController@getRoomLockStatus');
        Route::any('/rooms/{id}/update-locked-status', 'ApiController@updateRoomLockStatus');
    });
});

Error:

XMLHttpRequest cannot load http://api.domain.uk/ajax/rooms/1/update-locked-status. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://ice.domain.uk' is therefore not allowed access. The response had HTTP status code 500.

Middleware:

namespace App\Http\Middleware;

use Closure;

class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
    }
}

Ajax:

function toggleDoors(roomId) {
    $.ajax({
        url: 'http://api.domain.uk/ajax/rooms/' + roomId + '/update-locked-status',
        type: "POST",
        success: function(data) {
            alert(data);
        }
    });
}

ApiController:

<?php
namespace App\Http\Controllers\Api;

use Auth;
use App\User;
use App\Http\Controllers\Controller;
use Validator;
use Redirect;
use Illuminate\Http\Request;
use App\Database\Frontend\Other\Rooms;

class ApiController extends Controller
{
    public function getRoomLockStatus($id) {
        $room = Rooms::find($id);

        if ($room == null) {
            return response('bad request', 400);
        } 
        else {
            return $room->rp_locked;
        }
    }

    public function updateRoomLockStatus(Request $request, $id) {
        $room = Rooms::find($id);

        if ($room == null) {
            return response('bad request', 400);
        } 

        $room->rp_locked = $room->rp_locked == '1' ? '0' : '1';
        $room->save();

        $responseText = $room->rp_locked == '1' ?
            'Your doors have been locked.' : 'Your doors have been unlocked.';

        return response($responseText, 200);
    }
}
VoiD HD
  • 699
  • 2
  • 8
  • 18
  • Did you update the $routeMiddleware variable in /app/Http/Kernel.php to include the new cors middleware? – user1669496 Apr 14 '17 at 12:26
  • 1
    Yes I did...... – VoiD HD Apr 14 '17 at 12:30
  • I found a solution that allows to apply your CORS library only on a subset of endpoints (for security reason) without using any external lib: https://stackoverflow.com/questions/34748981/laravel-5-2-cors-get-not-working-with-preflight-options/64654589#64654589 – Martin Braun Nov 02 '20 at 23:11

9 Answers9

8

See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS#Preflighted_requests_in_CORS

If your problem in OPTIONS method.

Kernel::$routeMiddleware not working in Laravel 5.4 for request method OPTIONS, see https://github.com/laravel/framework/blob/v5.4.0/src/Illuminate/Routing/RouteCollection.php#L214. For use CORS middleware, enable it in Kernel::$middleware array. It is not good, but no other way.

For example, I use next middleware class for SPA and API, attention, it is not middleware 'cors' for routes

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

/**
 * OptionsCorsResponse middleware - add CORS headers if request method OPTIONS
 */
class OptionsCorsResponse
{
    /**
     *
     * @param Request $request
     * @param Closure $next
     * @return Response
     */
    public function handle($request, Closure $next)
    {
        /* @var $response Response */
        $response = $next($request);
        if (!$request->isMethod('OPTIONS')) {
            return $response;
        }
        $allow = $response->headers->get('Allow'); // true list of allowed methods
        if (!$allow) {
            return $response;
        }
        $headers = [
            'Access-Control-Allow-Methods' => $allow,
            'Access-Control-Max-Age' => 3600,
            'Access-Control-Allow-Headers' => 'X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept',
        ];
        return $response->withHeaders($headers);
    }
}

and enable it in App\Http\Kernel

protected $middleware = [
    // ...
    \App\Http\Middleware\OptionsCorsResponse::class,
];

Origin 'http :// ice . domain . uk' is therefore not allowed access. The response had HTTP status code 500.

Debug your code, because it generate some exception. Use any REST client with OPTIONS method.

6

In the CORS, browser first send the OPTIONS request to the specified route.

In CORS, a preflight request with the OPTIONS method is sent, so that the server can respond whether it is acceptable to send the request with these parameters: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS

So Change your middleware like this:

public function handle($request, Closure $next)
    {
        if ($request->isMethod('OPTIONS')){
            $response = Response::make();
        } else {
            $response = $next($request);
        }
        return $response
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
    }

If you want to allow other headers to your routes, please add them in the 'Access-Control-Allow-Headers' header field.

Pankit Gami
  • 2,523
  • 11
  • 19
  • This doesn't work either. I still get the same error I did with the other guys answer. XMLHttpRequest cannot load api.domain.uk/ajax/rooms/1/update-locked-status. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'ice.domain.uk'; is therefore not allowed access. – VoiD HD Apr 14 '17 at 13:41
  • Did you change the prefix of your route to `ajax`? – Pankit Gami Apr 14 '17 at 13:43
  • Did you remove the package or still using it? – Pankit Gami Apr 14 '17 at 13:44
  • I removed the package. – VoiD HD Apr 14 '17 at 13:45
  • You must need to send `Response` object from your controller. If you are dumping anything in your controller or anywhere in between you need to remove it. – Pankit Gami Apr 14 '17 at 13:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/141723/discussion-between-pankit-gami-and-void-hd). – Pankit Gami Apr 14 '17 at 13:50
5

You can do it easily by adding headers in bootstrap/app.php

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: *');
header('Access-Control-Allow-Headers: *');
Mahfuz Shishir
  • 841
  • 1
  • 7
  • 24
2

If none of this working, add cors on apache virtual host configuration (If you use virtual host).

Go to /etc/apache2/sites-available and add something like this gist

then sudo a2ensite example.conf and sudo service apache2 reload ;)

LuckyTuvshee
  • 1,166
  • 8
  • 16
1

I ran into a sudden CORS issue recently that was not caused by CORS header configuration, I discovered the following:

There are Red Herring scenarios that can also cause a CORS Cross Origin error to display and yet not have anything to do with CORS configuration, It is a result of when CORS is handled by middleware and something else prevents it from being triggered.

The following can indirectly cause the error to display in a browser response:

  • A PHP error in a Middleware class.
  • return $next($request); not being fired in middleware class method handle.
  • Route::middleware in web or api router configs reference a middleware that no longer exists or is miss spelt.
  • Same as above point but middleware specified in a Controller with $this->middleware();

Any of these can prevent a Cors middleware from ever being fired because the app exits too early and never sets the headers and thus results in a CORS error instead of a 500 Server Header error as a result of bad middleware files or bad references to middleware.

If you are certain you have configured CORS correctly then you should check your PHP error logs for Middleware errors.

Marc
  • 5,109
  • 2
  • 32
  • 41
1

I had a problem handling files using the withHeaders() method, so thanks to the tips below i came up with this working code:

/**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->isMethod('OPTIONS'))
        {
            return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }

        $response = $next($request);
        $response->headers->set('Access-Control-Expose-Headers', 'Content-Disposition');
        $response->headers->set('Access-Control-Allow-Origin', 'http://localhost:8080','http://localhost','https://edu.pilateswien.org');
        $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');

        //return $response->withHeaders($headers);
        return $response;
    }
mwx
  • 91
  • 1
  • 4
1

I am using Laravel 6 and up. This url helped me in solving my CORS issue: https://medium.com/@petehouston/allow-cors-in-laravel-2b574c51d0c1

Use this code instead of code in the url:

<?php

namespace App\Http\Middleware;

use Closure;

class Cors
{
    public function handle($request, Closure $next)
    {

          return $next($request)
              ->header('Access-Control-Allow-Origin', '*')
              ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
              ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type,X-Token-Auth, Authorization');
    }
}

Also, if you want to use middleware through the entire application then you need to make changes in Kernel.php:

protected $middleware = [
\App\Http\Middleware\Cors::class, //add this line to $middleware variable
]
Bimal
  • 865
  • 10
  • 18
0

Sergei is right, the problem is caused because of the preflight request: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS#Preflighted_requests_in_CORS

Thus adding a middleware only to a group of endpoints won't work, because the preflight is using the OPTION method and not the GET method.

This package solves exactly this issue, by having a middleware that is put in your kernel for all routes, but then you filter the routes where you want to allow CORS in config/cors.php. Thus you can also handle the preflight request that come with the option method.

In short, install the package:

composer require fruitcake/laravel-cors

put the middleware in your middleware array:

protected $middleware = [
  \Fruitcake\Cors\HandleCors::class,
    // ...
];

publish the config file:

php artisan vendor:publish --tag="cors"

And specify in paths inside config/cors which routes (or only a single route) you want to allow:

'paths' => ['api/*'],

See also this blog post for more.

Adam
  • 25,960
  • 22
  • 158
  • 247
-1

Just add this code on your routes

header('Access-Control-Allow-Origin: http://yourdomain.com/');
Pervez
  • 516
  • 6
  • 10