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 AppHttpMiddleware;
use Closure;
class Cors
{
/**
* Handle an incoming request.
*
* @param IlluminateHttpRequest $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 AppHttpControllersApi;
use Auth;
use AppUser;
use AppHttpControllersController;
use Validator;
use Redirect;
use IlluminateHttpRequest;
use AppDatabaseFrontendOtherRooms;
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);
}
}
Background
UPDATE 1 August 2020
This article was written when Laravel 6 was out and before first class CORS support was built into Laravel 7. Due to the integrated nature of CORS to an application we generally recommend you rather follow the official documentation when debugging Laravel issues with CORS. If you have’t upgraded to Laravel 7 yet, you are going to rapidly fall behind so we seriously recommend you do that. We have much success with Laravel Shift, but sometimes it a good learning exercise just to do it yourself.
Original Article
This article was compiled during a Laravel Development troubleshooting session and might not apply to other CORS sessions.
You might find yourself in a situation where you are trying to post from a REMOTE host to a LOCALHOST, especially during testing.
HTTPS should be set up properly. But instead of the post coming in, you get the following:
Access to XMLHttpRequest at 'https://sitename.test/api/v1/endpoint' from origin 'https://yourdomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is a challenging problem to Google due to conflicting information on the internet. Here are a few options:
Option 1 (challenging)
Don’t do the Axois request through your browser. Do a server API and then use a CURL / Guzzlehttp request.
Option 2 (how to troubleshoot)
This error can come from many locations. Most notably you have to establish if you are having this problem because the server is blocking you, or if you can simply do something on the client to avoid it. If you don’t have control over the server then you will have limited options. So in this troubleshooting, try to determine if the server or the client is causing the problem.
If you are using Laravel, and you have server control, then the solution might be the Laravel CORS library by Barry vd Heuvel:
https://github.com/barryvdh/laravel-cors
Here are brief instructions for installing this package
composer require barryvdh/laravel-cors
Then:
protected $middleware = [ // ... BarryvdhCorsHandleCors::class, ];
Then:
$ php artisan vendor:publish --provider="BarryvdhCorsServiceProvider"
Make sure your Laravel app is setup with Passport. Here is a summary of most of the steps.:
composer require laravel/passport
php artisan migrate
php artisan passport:install
Add HasApiTokens to the user model:
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
In boot method of AuthServiceProvider add the Password::routes(); line as below:
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'AppModel' => 'AppPoliciesModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
Update the guards in config/auth.php the ‘api’ one make the driver ‘passport’.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
If you’ve done all of the above, and you’re consuming your own API via your front-end, you’re in good stead because all you have to do now is:
https://laravel.com/docs/5.7/passport#consuming-your-api-with-javascript
'web' => [
// Other middleware...
LaravelPassportHttpMiddlewareCreateFreshApiToken::class,
],
Check the docs if it’s still not working 🙂
When not Consuming Own API
If you’re not consuming your own API and coming from another URL, e.g. a remote URL to your local testing URL.
You have to continue the Laravel guide, including getting those Vue components and tokens working.
php artisan vendor:publish —tag=passport–migrations
php artisan vendor:publish --tag=passport-components
resources/js/app/js
Vue.component(
'passport-clients',
require('./components/passport/Clients.vue').default
);
Vue.component(
'passport-authorized-clients',
require('./components/passport/AuthorizedClients.vue').default
);
Vue.component(
'passport-personal-access-tokens',
require('./components/passport/PersonalAccessTokens.vue').default
);
npm run dev
Then create a new page and add these components:
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>
Then login, and create a user and a token. Below is a code sample but please note we have removed the very long bearer token and substituted it with very_long_bearer_token.
In your code:
axios.defaults.headers.common['Authorization'] = "Bearer " + 'very_long_bearer_token';
axios.post('https://project.test/api/v1/subscribe', {
name: this.name,
email: this.email
})
.then(function (response) {
page.output = response.data;
})
.catch(function (error) {
page.output = error;
});
}
If you’ve made it this far, congratulations! You’ve just digested a lot of information and I hope this will assist you in fixing the CORS issue. Please leave a comment or get in touch if you need additional help.
- Familiarity with the Laravel framework
- An existing project with Laravel framework version >= 7.0.
- Vue CLI installed.
- This blog post dives into some of the security issues around misconfiguring CORS
- The MDN docs have an article on it with relevant HTTP headers.
- Email: michaelsokoko@gmail.com
- Github: https://github.com/idoqo
- Twitter: https://twitter.com/firechael
By default, browsers implement a same-origin policy that prevents scripts from making HTTP requests across different domains. Cross-Origin Resource Sharing (CORS for short) provides a mechanism through which browsers and server-side applications can agree on requests that are allowed or restricted.
From version 7, the Laravel framework comes with first-party support for sending CORS headers using Middlewares.
In this tutorial, we’ll be building a simple Vue.js app powered by Laravel to learn about CORS. We will take a deep-dive into the various configuration options needed by Laravel to handle CORS OPTIONS requests and see how some of these options affect our application.
Prerequisites
In order to complete this tutorial, you will need the following:
Getting Started
Create a new folder to hold both the Laravel API and the Vue project, and create the Laravel project with the commands below:
$ mkdir laravel-cors && cd laravel-cors
$ laravel new server
The Laravel project created includes a generic CORS configuration at $APP_FOLDER/config/cors.php which can be fine-tuned to meet your application needs. Here is a sample configuration file generated for a new Laravel application (without the comments).
<?php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
To inspect the API response headers from the Vue frontend, modify the `exposed_headers` array to use the wildcard ` character as shown below:
'exposed_headers' => ['*'],
Next, we will set up our API routes to respond to requests with fake data. Open the api routes file at `routes/api.php`, and replace its content with the code block below.
<?php
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
Route::get('/items', function(Request $request) {
$data = [
[
"id" => 7,
"name" => "na like this",
"description" => "",
"created_at" => "2020-07-26T05:53:00.376501Z",
"updated_at" => "2020-07-26T05:53:00.376501Z"
], [
"id" => 5,
"name" => "write a book",
"description" => "hohoho",
"created_at" => "2020-07-26T05:47:00.908706Z",
"updated_at" => "2020-07-26T05:53:00.376501Z"
]
];
return response()->json($data);
});
Route::get('/items/{id}', function(Request $request) {
$data = [
'id' => 1,
'name' => "Swim across the River Benue",
'description' => "ho ho ho",
'created_at' => "2020-07-26T22:31:04.49683Z",
'updated_at' => "2020-07-26T22:31:04.49683Z"
];
return response()->json($data);
});
Route::post('/items', function(Request $request) {
$data = [
'id' => 1,
'name' => "Swim across the River Benue",
'description' => "ho ho ho",
'created_at' => "2020-07-26T22:31:04.49683Z",
'updated_at' => "2020-07-26T22:31:04.49683Z"
];
return response()->json($data, 201);
});
Setting up the Vue Frontend
Create a new Vue project in the laravel-cors folder by running vue create frontend. Select the default preset. In the created frontend folder, open the HelloWorld.vue file (at src/components/HelloWorld.vue) and replace its contents with the code block below:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>Open your Browser console to see the headers sent in by Laravel.</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
mounted() {
let req = new Request("http://localhost:8000/api/items", {
method: "get",
});
fetch(req).then((response) => {
for (let pair of response.headers.entries()) {
console.log(pair[0] + ": " + pair[1]);
}
});
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
The code above makes an HTTP request to the /items route we created back in our Laravel application and logs all of the response headers it receives.
To try things out, serve the Laravel application by running php artisan serve in the server folder from your terminal.
While the server is running, launch the Vue app by changing into the frontend directory and running yarn serve.
Visit the Local URL reported by yarn serve in your browser and open the page console.
You should see the response headers in your console as shown in the screenshot below:
Enabling CORS for a Route Prefix
The paths option lets you specify the routes or resource paths for which you want to send CORS headers. In the configuration above, CORS headers are enabled for all routes with the “api” prefix. Removing that prefix or changing it to something else (e.g ‘paths’ => [‘endpoints/*’]) disables CORS for our API endpoints and we get an error similar to the one shown below:
Paths could be exact string matches (e.g /public/img/photo.img) or specified using the “*” wildcard (e.g /public/* or /api/*). Additionally, you can enable CORS for files in a given folder and all of its subfolders using the pattern: public/**/*.
Looking up Allowed HTTP Methods
allowed_methods specifies the HTTP request methods allowed when accessing a resource. The methods you add here are returned in the “Allowed-Methods” header when a client makes a preflight request to the Laravel application. By default, Laravel enables CORS for all HTTP methods (by using the “*” value).
For example, replacing the wildcard “*” character in the allowed_methods with “POST” will break our frontend code (since it is making “GET” requests to the server).
Restricting Allowed Hosts
The allowed_origins dictates the “origins” that are allowed to access the resources (origin here refers to the combination of scheme, domain, and port of the URL). Like the options above, it also allows for wildcard matching (e.g *example.com will allow example.com and any of its subdomains to access the resource). It is set to allow all origins by default.
NOTE: When not using a wildcard, the origin must be specified in full (e.g http://example.com is valid, example.com is not).
Restricting Allowed Hosts with Regular Expression
Using the allowed_origins_patterns option, you can specify your allowed origins using Regular Expression. It is useful when you want match patterns more complex than the “*” wildcard supported by allowed_origins. The values provided here must be a valid preg_match() pattern. Also, take care to avoid being accidentally inclusive. For example, /https?://example.com/ is valid for both https://example.com and https://example.com.hackersdomain.com. A better pattern is /https?://example.com/?z/ which limits it to domains ending with example.com (or with an optional slash at the end).
Configuring Allowed Headers
The allowed_headers option defines the HTTP headers that will be allowed in the actual CORS request. It sets the Access-Control-Allow-Headers header sent as a response to preflight requests containing Access-Control-Request-Headers. The generated configuration defaults to allowing all HTTP headers as well.
Exposing Custom Headers
Using exposed_headers, you can allow your API clients to access custom HTTP headers that are not safe-listed by CORS by default. For example, you may want to expose the X-RateLimit-Remaining and X-RateLimit-Limit headers set by Laravel’s throttle middleware to indicate how many more requests a client can make within the given time frame.
There are only seven HTTP headers that are safe-listed by CORS (i.e., Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma) so you will have to include any other header your application needs to expose in the exposed_headers array.
Caching CORS Responses
The value of the max_age option determines how long (in seconds) a client can cache the response of a preflight request. Laravel sets it to 0 by default. Browsers will ignore the max_age value if it is longer than their maximum limit (24 hours for Firefox, 2 hours for Chromium >= v76).
HTTP Sessions over CORS
supports_credentials allows sending cookies and sessions (which rely on cookies also) over CORS requests. It sets the Access-Control-Allow-Credentials and cannot be true when all origins are allowed (i.e., when allowed_origins is the wildcard (*) character).
Conclusion
Configuring CORS can be a chore sometimes. Laravel now makes it easier with an “out-of-the-box” setup by leveraging the laravel-cors package.
If you are looking to explore CORS generally,
Michael Okoko is a software engineer and computer science student at Obafemi Awolowo University, Nigeria. He loves open source and is mostly interested in Linux, Golang, PHP, and fantasy novels! You can reach him via:


