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 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 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 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 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 commented Jun 22, 2015

Thanks for this solving

@milroyfraser

This comment has been minimized.

Copy link

milroyfraser commented Aug 1, 2015

The best solution I have found thus far!

@donpaul120

This comment has been minimized.

Copy link

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 commented Aug 17, 2015

Killer! Thanks!

@tiagojlara

This comment has been minimized.

Copy link

tiagojlara commented Aug 23, 2015

Thanks!

@alebonyo

This comment has been minimized.

Copy link

alebonyo commented Aug 24, 2015

Thank you very much!

@brunocoelhor

This comment has been minimized.

Copy link

brunocoelhor commented Sep 25, 2015

Thanks \o/

@bcaballero

This comment has been minimized.

Copy link

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 commented Nov 9, 2015

Thanks, I had two hours with this problem.

@Nebudev

This comment has been minimized.

Copy link

Nebudev commented Nov 11, 2015

You are a God. tks

@hedii

This comment has been minimized.

Copy link

hedii commented Jan 14, 2016

👍 thanks

@amansura

This comment has been minimized.

Copy link

amansura commented Feb 4, 2016

Thanks a lot for this lovely snippet

@davidnknight

This comment has been minimized.

Copy link

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 commented Mar 3, 2016

Amazing!! 😊

@imjohnbon

This comment has been minimized.

Copy link

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 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 commented Aug 4, 2016

Awesome, this solved my problem. Thanks!

@dmodaii

This comment has been minimized.

Copy link

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 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 commented Aug 21, 2016

Perfect!

@adrianjverav

This comment has been minimized.

Copy link

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 commented Oct 28, 2016

I had to use

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

This comment has been minimized.

Copy link

ikashifullah commented Dec 28, 2016

Perfect solution, you are genius!

@anoopmd

This comment has been minimized.

Copy link

anoopmd commented Jan 18, 2017

Awesome! Thank you so much.

@troccoli

This comment has been minimized.

Copy link

troccoli commented Feb 17, 2017

Thank you so much. Saved my life.

@coolhihi

This comment has been minimized.

Copy link

coolhihi commented Feb 23, 2017

it helped me. thank you!

@herveguetin

This comment has been minimized.

Copy link

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 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 commented Apr 4, 2017

Thanks man, this works great!

@maclonghorn

This comment has been minimized.

Copy link

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 commented Apr 28, 2017

Thank you so much.

@elporfirio

This comment has been minimized.

Copy link

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 commented Jul 7, 2017

Thanks @rgehan. This modification works well

@rodralme

This comment has been minimized.

Copy link

rodralme commented Jul 28, 2017

Thanks @rgehan. You save my life.

@lazarohcm

This comment has been minimized.

Copy link

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 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 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 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 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 commented Sep 27, 2017

and now POST 404 not found

@bosskmk

This comment has been minimized.

Copy link

bosskmk commented Oct 7, 2017

Thank you.

@CarlosZM

This comment has been minimized.

Copy link

CarlosZM commented Oct 18, 2017

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

@thalassat

This comment has been minimized.

Copy link

thalassat 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 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 commented Nov 19, 2017

Wow...Finally... Thanks a lot

@nusendra

This comment has been minimized.

Copy link

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 commented Dec 29, 2017

Thanks a lot mate.. 👍

@lmj0011

This comment has been minimized.

Copy link

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 commented Sep 7, 2019

Thanks for summing it up @nusendra

@nusendra

This comment has been minimized.

Copy link

nusendra commented Sep 8, 2019

my pleasure bud @choyan

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.