Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Lumen with CORS and OPTIONS requests
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
/**
* If the incoming request is an OPTIONS request
* we will register a handler for the requested route
*/
class CatchAllOptionsRequestsProvider extends ServiceProvider {
public function register()
{
$request = app('request');
if ($request->isMethod('OPTIONS'))
{
app()->options($request->path(), function() { return response('', 200); });
}
}
}
<?php namespace App\Http\Middleware;
class CorsMiddleware {
public function handle($request, \Closure $next)
{
$response = $next($request);
$response->header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE');
$response->header('Access-Control-Allow-Headers', $request->header('Access-Control-Request-Headers'));
$response->header('Access-Control-Allow-Origin', '*');
return $response;
}
}

Register the CatchAllOptionsRequestsProvider service provider in bootstrap/app.php which will check the incoming request and response successfully if it is an OPTIONS request.

Add the CorsMiddleware to the $app->middleware([ array in bootstrap/app.php which will attach the following CORS headers to all responses:

  • allow all headers
  • allow requests from all origins
  • allow all the headers which were provided in the request
@crisu83

This comment has been minimized.

Copy link

@crisu83 crisu83 commented May 11, 2015

Thanks for this. I think it's a brilliant idea to use a service provider to interrupt the OPTIONS requests.

@mattpatterson94

This comment has been minimized.

Copy link

@mattpatterson94 mattpatterson94 commented May 22, 2015

You're a legend man. Been thinking it was a better idea to go with one of the packages that are out there, unfortunately the ones that are for laravel don't work for lumen and the lumen ones just don't seem to function properly based on all the OPTIONS stuff.

This is simple and sweet. Huge applaud to you! You should chuck it in a composer package, it would definitely rate well. Maybe just add the allowed methods/headers into the .env.

@rimantoro

This comment has been minimized.

Copy link

@rimantoro rimantoro commented Jun 11, 2015

Dude, I've been search for this on the last two days.

My client build with Angular and always have an prefligh request with OPTIONS method. After 2 days with no result in Angular side, then I think, perhaps its on the Lumen side, then I found your solution and now its work.

Thanks a lot.

@RynnHeldeD

This comment has been minimized.

Copy link

@RynnHeldeD RynnHeldeD commented Jun 20, 2015

Hi Sir !

Thanks a lot for your solution, my friend and I had some CORS problem with our angular application and OPTIONS method. We tried with your code and it works like a charm !

@Anton-melezhik

This comment has been minimized.

Copy link

@Anton-melezhik Anton-melezhik commented Jun 22, 2015

Thanks for this solving

@milroyfraser

This comment has been minimized.

Copy link

@milroyfraser milroyfraser commented Aug 1, 2015

The best solution I have found thus far!

@donpaul120

This comment has been minimized.

Copy link

@donpaul120 donpaul120 commented Aug 9, 2015

Lord!!!!! I have to comment.... This is the best solution, BEST!! BEST!! BEST!!... really saved me a lot.
Thanks sooo sooo much :: #iBow

@vmitchell85

This comment has been minimized.

Copy link

@vmitchell85 vmitchell85 commented Aug 17, 2015

Killer! Thanks!

@tiagojlara

This comment has been minimized.

Copy link

@tiagojlara tiagojlara commented Aug 23, 2015

Thanks!

@alebonyo

This comment has been minimized.

Copy link

@alebonyo alebonyo commented Aug 24, 2015

Thank you very much!

@brunocoelhor

This comment has been minimized.

Copy link

@brunocoelhor brunocoelhor commented Sep 25, 2015

Thanks \o/

@bcaballero

This comment has been minimized.

Copy link

@bcaballero bcaballero commented Oct 19, 2015

@danharper

This worked on my local machine with two different vhost config. But when I uploaded the updates on live servers headers were not set on the second xhr.

1st response

Access-Control-Allow-Credentials:*
Access-Control-Allow-Headers:accept, content-type, x-csrf-token
Access-Control-Allow-Methods:*
Access-Control-Allow-Origin:*
Cache-Control:no-cache
Cache-Control:max-age=0
Connection:keep-alive, Keep-Alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Mon, 19 Oct 2015 01:02:52 GMT
Expires:Mon, 19 Oct 2015 01:02:52 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.10 (Debian)
Transfer-Encoding:chunked
Vary:Accept-Encoding

2nd response header

Cache-Control:no-cache, private
Connection:keep-alive, close
Content-Type:text/html; charset=UTF-8
Date:Mon, 19 Oct 2015 01:02:52 GMT
Server:Apache/2.4.10 (Debian)
Transfer-Encoding:chunked

Any ideas why is this not working on different servers?

@spaky08

This comment has been minimized.

Copy link

@spaky08 spaky08 commented Nov 9, 2015

Thanks, I had two hours with this problem.

@Nebudev

This comment has been minimized.

Copy link

@Nebudev Nebudev commented Nov 11, 2015

You are a God. tks

@hedii

This comment has been minimized.

Copy link

@hedii hedii commented Jan 14, 2016

👍 thanks

@amansura

This comment has been minimized.

Copy link

@amansura amansura commented Feb 4, 2016

Thanks a lot for this lovely snippet

@davidnknight

This comment has been minimized.

Copy link

@davidnknight davidnknight commented Feb 11, 2016

I don't understand why you're dividing code over 2 files when all of this can be handled in the single middleware, with no need for a provider?

<?php namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $headers = [
            'Access-Control-Allow-Origin'      => '*',
            'Access-Control-Allow-Methods'     => 'POST, GET, OPTIONS, PUT, DELETE',
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Max-Age'           => '86400',
            'Access-Control-Allow-Headers'     => 'Content-Type, Authorization, X-Requested-With'
        ];

        if ($request->isMethod('OPTIONS'))
        {
            return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }

        $response = $next($request);
        foreach($headers as $key => $value)
        {
            $response->header($key, $value);
        }

        return $response;
    }
}
@carolinesalib

This comment has been minimized.

Copy link

@carolinesalib carolinesalib commented Mar 3, 2016

Amazing!! 😊

@imjohnbon

This comment has been minimized.

Copy link

@imjohnbon imjohnbon commented Apr 8, 2016

Can anyone explain why we have the separate provider for catching OPTIONS requests? Why not include them with the other request types in 'Access-Control-Allow-Methods' in our middleware?

Basically I'm trying to figure out why we're treating options differently. As doing so seems to break preflight requests when you make calls via Chrome.

@falmar

This comment has been minimized.

Copy link

@falmar falmar commented Jun 23, 2016

@imjohnbon from my understanding it is because lumen does not allow OPTIONS method and will return status response 405 MethodNotAllowed unless you explicitly add it your routes $app->options('my-route', function(){}), that is why the request do not hit the middleware. in my case this was happening using react and I was stuck for almost one hour trying to figure out what was wrong, until I found this and everything make sense.

But this service provider was not working without adding extra headers apparently the preflight request need this headers in response as well:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Headers: Content-Type, Origin

Otherwise I would the following errors:

  • Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'my-host' is therefore not allowed access.
  • Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response

But Following this amazing CORS tutorial: http://www.html5rocks.com/en/tutorials/cors/

Heres the ServiceProvider.php with the changes

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $request = app('request');

        // ALLOW OPTIONS METHOD
        if($request->getMethod() === 'OPTIONS')  {
            app()->options($request->path(), function () {
                return response('OK',200)
                    ->header('Access-Control-Allow-Origin', '*')
                    ->header('Access-Control-Allow-Methods','OPTIONS, GET, POST, PUT, DELETE')
                    ->header('Access-Control-Allow-Headers', 'Content-Type, Origin');                    
            });
        }
    }
}

And here is the middleware

<?php

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $response->header('Access-Control-Allow-Origin','*');

        return $response;
    }
}
@robtejeda

This comment has been minimized.

Copy link

@robtejeda robtejeda commented Aug 4, 2016

Awesome, this solved my problem. Thanks!

@dmodaii

This comment has been minimized.

Copy link

@dmodaii dmodaii commented Aug 9, 2016

sorry , may I ask how to use two files in app.php, I don't know how to register these in bootstrap/app.php

@IonutBajescu

This comment has been minimized.

Copy link

@IonutBajescu IonutBajescu commented Aug 14, 2016

You don't have to use a service provider as long as you configured the middleware as a global middleware rather than
a route middleware.

@mariocsantos

This comment has been minimized.

Copy link

@mariocsantos mariocsantos commented Aug 21, 2016

Perfect!

@adrianjverav

This comment has been minimized.

Copy link

@adrianjverav adrianjverav commented Sep 16, 2016

Thanks brother. Finally the API I'm developing in Lumen I can consume externally with Vue.js. The error that had me on the edge I could solve the madness . Greetings from Venezuela.

@jeroenherczeg

This comment has been minimized.

Copy link

@jeroenherczeg jeroenherczeg commented Oct 28, 2016

I had to use

$response->headers->set($key, $value);
@ikashifullah

This comment has been minimized.

Copy link

@ikashifullah ikashifullah commented Dec 28, 2016

Perfect solution, you are genius!

@anoopmd

This comment has been minimized.

Copy link

@anoopmd anoopmd commented Jan 18, 2017

Awesome! Thank you so much.

@troccoli

This comment has been minimized.

Copy link

@troccoli troccoli commented Feb 17, 2017

Thank you so much. Saved my life.

@coolhihi

This comment has been minimized.

Copy link

@coolhihi coolhihi commented Feb 23, 2017

it helped me. thank you!

@herveguetin

This comment has been minimized.

Copy link

@herveguetin herveguetin commented Feb 24, 2017

After implementing this, I was still having the "405 Method Not Allowed" error. Just because I didn't implement the 'options' method in my routes file (routes/web.php)...

So adding this method with an empty Closure did the trick:

$app->options('/path/with/allowed/options', function () {
    //
});

Also, by using regex in this route, it is possible to make this work for all URi (don't know if that's recommanded though... please yell if not):

$app->options('{path:.+}', function () {
    //
});
@rgehan

This comment has been minimized.

Copy link

@rgehan rgehan commented Feb 28, 2017

I've been having the same issues as all of you with Lumen 5.4. It seems to have been caused by a recent change in the ServiceProviders code (see laravel/lumen-framework#568)

For it to work, you now have to intercept the OPTIONS requests directly into the CorsMiddleware, and not in a Service Provider.

<?php

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
		//Intercepts OPTIONS requests
		if($request->isMethod('OPTIONS')) {
			$response = response('', 200);
		} else {
			// Pass the request to the next middleware
			$response = $next($request);
		}

		// Adds headers to the response
		$response->header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE');
		$response->header('Access-Control-Allow-Headers', $request->header('Access-Control-Request-Headers'));
		$response->header('Access-Control-Allow-Origin', '*');

		// Sends it
		return $response;
	}
}

There's no need to add any route (as @herveguetin suggested) since all the interception is done in a middleware.

I registered this middleware as global, but I think it will work on a route basis too.

@shaneparsons

This comment has been minimized.

Copy link

@shaneparsons shaneparsons commented Apr 4, 2017

Thanks man, this works great!

@maclonghorn

This comment has been minimized.

Copy link

@maclonghorn maclonghorn commented Apr 19, 2017

I was still having problems, and found that you have to Enable Headers and CORS in Apache

@fifths

This comment has been minimized.

Copy link

@fifths fifths commented Apr 28, 2017

Thank you so much.

@elporfirio

This comment has been minimized.

Copy link

@elporfirio elporfirio commented May 12, 2017

@rgehan solutions works perfectly adding a Middleware. Im Working Lumen 5.4 in vhost (custom domain) and consuming via Angular4 (locahost)

===
UPDATE > @rgehan your solution doesn't work on anonymous function - download method

    $app->get('images/covers/{image}', function($image = null){
        $path = storage_path().'/app/covers/';
        if(file_exists($path . $image)){
            return response()->download($path . $image);
        }
    });
@kakoma

This comment has been minimized.

Copy link

@kakoma kakoma commented Jul 7, 2017

Thanks @rgehan. This modification works well

@rodralme

This comment has been minimized.

Copy link

@rodralme rodralme commented Jul 28, 2017

Thanks @rgehan. You save my life.

@lazarohcm

This comment has been minimized.

Copy link

@lazarohcm lazarohcm commented Aug 15, 2017

Thank you mate!

I had to change my response headers a little to make it works on here. The allowed headers now looks like this
'Access-Control-Allow-Headers' => 'access-control-allow-origin,authorization,content-type'

@vamidi

This comment has been minimized.

Copy link

@vamidi vamidi commented Aug 25, 2017

Thank you so much this really helped me out!
Don't know if anyone had to do the same thing, but I had to do

$app->options('/card', ['middleware' => 'cors', '']);

$app->post('/card', ['middleware' => 'cors', 'uses' => 'CardController@createCard']);

to make it work maybe I missing a step.

@s1728k

This comment has been minimized.

Copy link

@s1728k s1728k commented Aug 30, 2017

hi thanks

It worked for me.

hi vamidi you need to register the middleware in bootstrap/app.php inside the app->middleware.

this is explained in top post.

@s1728k

This comment has been minimized.

Copy link

@s1728k s1728k commented Sep 8, 2017

hi I worked me for get request but for post request it is not working for me.

@s1728k

This comment has been minimized.

Copy link

@s1728k s1728k commented Sep 8, 2017

hi,

if you are using the IIS server by chance you set the HTTP request headers as below

Access-Control-Allow-Origin:*
Access-Control-Allow-Methods: 'HEAD, GET, POST, PUT, PATCH, DELETE'
Access-Control-Allow-Headers: 'Origin, Content-Type, X-Auth-Token';

then everything works fine.

@besabellacyrus

This comment has been minimized.

Copy link

@besabellacyrus besabellacyrus commented Sep 27, 2017

and now POST 404 not found

@bosskmk

This comment has been minimized.

Copy link

@bosskmk bosskmk commented Oct 7, 2017

Thank you.

@CarlosZM

This comment has been minimized.

Copy link

@CarlosZM CarlosZM commented Oct 18, 2017

Thanks @rgehan. You save my life.
@danharper try to update your solution with the @rgehan given

@txan2

This comment has been minimized.

Copy link

@txan2 txan2 commented Oct 27, 2017

Thanks @danharper.

I'm still concerned about the usage of Service Provider, isn't it meant to be used for registering things instead of handling things?

@saijayaprakash

This comment has been minimized.

Copy link

@saijayaprakash saijayaprakash commented Nov 4, 2017

Cors middleware is working but catchalloptionsprovider is not working and throwing the following error..Somebody,
screen shot 2017-11-05 at 12 53 59 am
please help me!!

@ccmelas

This comment has been minimized.

Copy link

@ccmelas ccmelas commented Nov 19, 2017

Wow...Finally... Thanks a lot

@nusendra

This comment has been minimized.

Copy link

@nusendra nusendra commented Dec 21, 2017

It works. Thanks
So This is the perfect solution for me

  1. Create CatchAllOptionsRequestsProvider.php to App\Providers folder.
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
/**
 * If the incoming request is an OPTIONS request
 * we will register a handler for the requested route
 */
class CatchAllOptionsRequestsProvider extends ServiceProvider {
  public function register()
  {
    $request = app('request');
    if ($request->isMethod('OPTIONS'))
    {
      app()->options($request->path(), function() { return response('', 200); });
    }
  }
}
  1. Then create CorsMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
		//Intercepts OPTIONS requests
		if($request->isMethod('OPTIONS')) {
			$response = response('', 200);
		} else {
			// Pass the request to the next middleware
			$response = $next($request);
		}

		// Adds headers to the response
		$response->header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE');
		$response->header('Access-Control-Allow-Headers', $request->header('Access-Control-Request-Headers'));
		$response->header('Access-Control-Allow-Origin', '*');

		// Sends it
		return $response;
	}
}
  1. In bootstrap/app.php
    add this
$app->middleware([
   App\Http\Middleware\CorsMiddleware::class
]);

$app->register(App\Providers\CatchAllOptionsRequestsProvider::class);

Thanks to @danharper and @rgehan

@zhacarias

This comment has been minimized.

Copy link

@zhacarias zhacarias commented Dec 29, 2017

Thanks a lot mate.. 👍

@lmj0011

This comment has been minimized.

Copy link

@lmj0011 lmj0011 commented May 5, 2018

thanks @davidnknight @rgehan

You're right, all of this Middleware logic can be placed just in the middleware file and not also in a ServiceProvider.

ref:

https://gist.github.com/danharper/06d2386f0b826b669552#gistcomment-1694593

https://gist.github.com/danharper/06d2386f0b826b669552#gistcomment-2013919

@choyan

This comment has been minimized.

Copy link

@choyan choyan commented Sep 7, 2019

Thanks for summing it up @nusendra

@nusendra

This comment has been minimized.

Copy link

@nusendra nusendra commented Sep 8, 2019

my pleasure bud @choyan

@yogenmohara

This comment has been minimized.

Copy link

@yogenmohara yogenmohara commented Feb 3, 2020

Works for me. Many Thanks. :)

@skylineCodes

This comment has been minimized.

Copy link

@skylineCodes skylineCodes commented Feb 21, 2020

Thanks very much for this

@rulatir

This comment has been minimized.

Copy link

@rulatir rulatir commented Apr 8, 2020

Could you please explain how this solution can possibly be correct? My understanding is that OPTIONS exists in the HTTP standard for a reason. Carelessly stubbing it to a no-op must eventually have Dire Consequences, right?

@hshahsahebi

This comment has been minimized.

Copy link

@hshahsahebi hshahsahebi commented Jun 8, 2020

My solution was creating CorsMiddleware as below:

<?php

/**
 * Location: /app/Http/Middleware
 */
namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
	/**
	 * Handle an incoming request.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @param  \Closure  $next
	 * @return mixed
	 */
	public function handle($request, Closure $next)
	{
		$headers = [
			'Access-Control-Allow-Origin' => '*',
			'Access-Control-Allow-Methods' => 'POST,GET,PATCH,PUT,DELETE,OPTIONS',
			'Access-Control-Max-Age' => '86400',
                        'Access-Control-Allow-Headers' => 'Content-Type,API-KEY'
                ];

		if ($request->isMethod('OPTIONS')) {
			return response()->json('', 200, $headers);
		}

		$response = $next($request);

		foreach ($headers as $key => $value) {
			$response->header($key, $value);
		}

		return $response;
	}
}

Then register the middleware with a name in bootstrap/app.php as below:

$app->routeMiddleware([
  'cors' => App\Http\Middleware\CorsMiddleware::class
]);

Then wrapping required routes between the defined middleware in routes/web.php as below, and adding an additional rule to handle preflight OPTIONS requests:

$router->group(['middleware' => 'cors'], function () use ($router) {
  //All the routes you want to allow CORS for

  $router->options('/{any:.*}', function (Request $req) {
    return;
  });
});

NOTE
Using Chrome, you can get a more detailed error message than Firefox, which will help you to fix the problem and exactly states why there is no Access-Control-Allow-Origin in the response header. Also, keep in mind that any kind of redirect is not allowed in the preflight request, so make sure you don't have such rule in your .htaccess or any other place.

@JhonLimbong17

This comment has been minimized.

Copy link

@JhonLimbong17 JhonLimbong17 commented Jun 30, 2020

My solution was creating CorsMiddleware as below:

<?php

/**
 * Location: /app/Http/Middleware
 */
namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
	/**
	 * Handle an incoming request.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @param  \Closure  $next
	 * @return mixed
	 */
	public function handle($request, Closure $next)
	{
		$headers = [
			'Access-Control-Allow-Origin' => '*',
			'Access-Control-Allow-Methods' => 'POST,GET,PATCH,PUT,DELETE,OPTIONS',
			'Access-Control-Max-Age' => '86400',
                        'Access-Control-Allow-Headers' => 'Content-Type,API-KEY'
                ];

		if ($request->isMethod('OPTIONS')) {
			return response()->json('', 200, $headers);
		}

		$response = $next($request);

		foreach ($headers as $key => $value) {
			$response->header($key, $value);
		}

		return $response;
	}
}

Then register the middleware with a name in bootstrap/app.php as below:

$app->routeMiddleware([
  'cors' => App\Http\Middleware\CorsMiddleware::class
]);

Then wrapping required routes between the defined middleware in routes/web.php as below, and adding an additional rule to handle preflight OPTIONS requests:

$router->group(['middleware' => 'cors'], function () use ($router) {
  //All the routes you want to allow CORS for

  $router->options('/{any:.*}', function (Request $req) {
    return;
  });
});

NOTE
Using Chrome, you can get a more detailed error message than Firefox, which will help you to fix the problem and exactly states why there is no Access-Control-Allow-Origin in the response header. Also, keep in mind that any kind of redirect is not allowed in the preflight request, so make sure you don't have such rule in your .htaccess or any other place.

I've tried all of the code, and LOL, no one work for me.
image

I'm still got this error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.