Enabling CORS On A Magento 2 Site Using An Nginx Server
Tags: php, linux, tutorial
Published: 2019-11-17 21:33:57


After spending a while fighting CORS issues using a headless Magento 2 setup, here is how I solved the issue using nginx.conf

Before jumping into a solution, I think it’s important to understand what is happening and why

### What is CORS?

CORS stands for Cross-Origin Resource Sharing and it basically controls which sites can access the resources of another site. If you are running applications on different subdomains and/or domains and they need to talk to each other, then you will need to setup CORS.

### How do we setup CORS?

You will need to send specific headers from the application that is being accessed. So for example, if your front end application is on one domain and it needs to access an API on another domain, you must setup the access headers on the API server.

### Docker Setup

This tutorial is only for a server running Nginx. If you already have Magento up and running and you just want to look at the final configuration file settings then you can skip this section.

For my setup I user docker compose using [Mark Shust’s configuration](https://github.com/markshust/docker-magento) and [Magento community edition is installed via composer](https://devdocs.magento.com/guides/v2.3/install-gde/composer.html). By default the docker-compose.dev.yml will mount the default Magento nginx.conf.sample file as your default configuration file. As we need a custom configuration file, copy the contents of **nginx.conf.sample** into a **nginx.conf** file in your root Magento folder. Update **docker-compose.dev.yml** replacing

 ./src/nginx.conf.sample:/var/www/html/nginx.conf:delegated

With

 ./src/nginx.conf:/var/www/html/nginx.conf:delegated

Now this will be the configuration file that will be used on the nginx server.

### Nginx Configuration Settings

#### CORS Preflight Request

When a site tries to request access to resources from another site, it will first of all send a [preflight request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request). This basically just asks the server if it will allow the request and it’s sent in the form of an OPTIONS request. This is why if you sent a POST request to another server, you will also see an OPTIONS request sent first.

So, we need to make sure that when an OPTIONS request is sent to our Nginx server, that we respond back with the correct headers. Here is an example:

{% gist https://gist.github.com/charlotte-bone/c941499f942346a62db6a91c437dd7c4 %}

* **Access-Control-Allow-Origin:** the domain allowed, * allows ALL domains. You cannot put multiple domains here (how to restrict to multiple domains is explained further down)
* **Access-Control-Allow-Methods:** the methods to allow CORS requests for. You might need PUT and DELETE too if you’re using the REST API, as I’m using GraphQL, I’m only using POST, GET and OPTIONS.
* **Access-Control-Allow-Headers:** the allowed request headers. If you were for example to send an Authorization header without it being listed here then your request will be rejected.
* **Access-Control-Allow-Credentials:** I’ve not included this in my configuration but I want to explain what this does. It will allow cookies to be sent with the request, for example if you need to authorise using a session cookie.

**Where do we put this code?**

You’ll notice entry points within the nginx.conf file. Most of the time you would put it in the root location, however in the Magento setup, this won’t work.

 location / {

 try_files $uri $uri/ /index.php$is_args$args

 }

This is because it uses **try_files** and if you set the headers above this then they get ignored because it checks for the index.php file and redirects if it exists. So you need to find the entry point for php files.

{% gist https://gist.github.com/charlotte-bone/6a8c22eb719eb3de7eb0c5ab4dedd561 %}

You’ll notice that I’ve set the cors origin url in a variable, this is because we also need to set the **Access-Control-Allow-Origin** outside of the OPTIONS request block, for all other requests. This has to be repeated because if you set headers in an if block, it overwrites the previous add_header statements, see [if is evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/) for an explanation.

If you restart nginx (bin/restart if you’re using the same docker configuration as me) then you’ll now be able to do CORS requests. However, it’s not very secure as it’s using * so it’s accessible to any domain! let’s restrict that!

### Restricting CORS to certain domains

As I mentioned above, sadly we can’t use multiple domains within the Access-Control-Allow-Origin header. So we have to do the logic outside of the header setting. Thankfully this is possible with simple regex:

{% gist https://gist.github.com/charlotte-bone/3cf9d38ceffc846e7944984660c9116b %}

Here, we’re simply setting the $cors_origin to the current request origin if it matches either [https://localhost:8000](https://localhost:8000/) or [https://mywebsite.com.](https://mywebsite.com.) You can modify this to allow multiple domains and subdomains.

Putting all of that together, here is my php entry point (**note: the configuration below the final add_header is the default magento configuration and may change with updates, so please do not just copy and paste everything**)

{% gist https://gist.github.com/charlotte-bone/9a77da39f40be3f6b12d877120745d81 %}

[Originally posted on Medium](https://medium.com/@charlotte.bone/enabling-cors-on-a-magento-2-site-using-an-nginx-server-481c8931f8b2)