★ Handling CORS in a Laravel application
Tags: laravel, php, cors, security
Published: 2020-01-02 22:54:40

---
title: ★ Handling CORS in a Laravel application
published: true
date: 2018-01-08 14:20:09 UTC
tags: laravel, php, cors, security
canonical_url: https://freek.dev/948-handling-cors-in-a-laravel-application
---

Recently we released [laravel-cors](https://github.com/spatie/laravel-cors). This package can add the necessary CORS headers of your Laravel app. In this post I'd like to give a quick explanation of what CORS is and how you can use the package.

## What is CORS #

Imagine that all JavaScript code for domain X running in a browser would be able to make http requests to an domain Y. Malious code on domain X would be able to interact with site Y without you knowing. In most circumstances you don't want this. Luckily all major browsers only allow sites to make requests against their own domain. They don't allow JavaScript code to make request against a sites on different domains. This is called [the same-origin policy](https://www.w3.org/Security/wiki/Same_Origin_Policy).

But there are some scenarios where you do want to allow that behaviour. Think of an API running on domain X that you want to consume via JavaScript running on domain Y. CORS stands for [cross-origin resource sharing](https://www.w3.org/TR/cors/). It's a standardized way to legitimately poke some holes in the same-origin policy.

## Simple requests #

When JavaScript running on domain X performs a `HEAD` `GET` or certain `POST` request (with `application/x-www-form-urlencoded`, `multipart/form-data` or `text/plain` to domain Y the browser will add an `Origin` header. The application running on domain Y can use this header to check if the request is permitted. If the server responds with a header `Access-Control-Allow-Origin` containing the domain X then the browser will conclude that request was allowed. If the server didn't do that most browsers won't allow the JS on domain X to perform any requests towards domain Y.

## All other requests #

All requests covered by the previous section will probably only be used to retrieve some data. All other ones such as certain `POST` requests, `PUT`, `PATCH`, `DELETE` will probably modify existing data on the server. For those kinds of request the browser will send a preflight request before doing the actualy request.

This preflight request using the `OPTIONS` verb. The browser will also send along the Origin header mentioned in the previous section. The server should return a response with the `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods` and `Access-Control-Max-Age headers` set. The `Access-Control-Allow-Methods` contains the HTTP verbs that are allowed. The `Access-Control-Max-Age` contains the time in seconds that no new preflight request should be sent.

## Using spatie/laravel-cors #

Our [spatie/laravel-cors](https://github.com/spatie/laravel-cors) package can handle verifying and setting all required headers for you. The package can be installed via Composer

```
composer require spatie/laravel-cors
```

After that you must register the `Cors` middleware

```
// app/Http/Kernel.php

protected $middleware = [
    ...
    \Spatie\Cors\Cors::class
];
```

Your application will now allow cross origin request coming from all domains. You probably want to publish the config file and set the `allow_origins` key to all domains you will allow requests from. Here's the default content of that config file.

```
return [
    /*
     * A cors profile determines which orgins, methods, headers are allowed for
     * a given requests. The `DefaultProfile` reads its configuration from this
     * config file.
     *
     * You can easily create your own cors profile.
     * More info: https://github.com/spatie/laravel-cors/#creating-your-own-cors-profile
     */
    'cors_profile' => Spatie\Cors\CorsProfile\DefaultProfile::class,

    /*
     * This configuration is used by `DefaultProfile`.
     */
    'default_profile' => [

        'allow_origins' => [
            '*',
        ],

        'allow_methods' => [
            'POST',
            'GET',
            'OPTIONS',
            'PUT',
            'PATCH',
            'DELETE',
        ],

        'allow_headers' => [
            'Content-Type',
            'X-Auth-Token',
            'Origin',
            'Authorization',
        ],

        /*
         * Preflight request will respond with value for the max age header.
         */
        'max_age' => 60 * 60 * 24,
    ],
];
```

## Using CORS profiles #

Imagine you want to specify allowed origins based on the user that is currently logged in. In that case the `DefaultProfile` which just reads the config file won't cut it. Fortunately it's very easy to write your own CORS profile. A valid CORS profile is any class that extends `Spatie\Cors\DefaultProfile`.

Here’s a quick example where it is assumed that you’ve already added an `allowed_domains` column on your user model:

```
namespace App\Services\Cors;

use Spatie\Cors\DefaultProfile;

class UserBasedCorsProfile extends DefaultProfile;
{
    public function allowOrigins(): array
    {
        return Auth::user()->allowed_domains;
    }
}
```

Don’t forget to register your profile in the config file.

```
// config/cors.php

 ...
 'cors_profile' => App\Services\Cors\UserBasedCorsProfile::class,
 ...
```

In the example above we’ve overwritten the `allowOrigins` method, but of course you may choose to override any of the methods present in `DefaultProfile`.

## In closing #

It might appear that this whole CORS business is something that aims to protect your server, but that is not the case. It’s primary purpose is to protect the user of the browser by not making any requests to unwanted other domains. Don’t see CORS as a mechanism to protect your server.

If you want to know more about our laravel-cors package, go check it out on GitHub. You could also opt to use Barry vd. Heuvel’s CORS package. Our package is a modern rewrite of the basic features of [Barry’s excellent one](https://github.com/barryvdh/laravel-cors). We created our own solution because we needed our configuration to be [very flexible](https://github.com/spatie/laravel-cors#creating-your-own-cors-profile).

Be sure to also check out the [packages our team has previously made](https://spatie.be/en/opensource). There’s probably something there that could be of use in your next project.