Enabling CORS On A Magento 2 Site Using An Nginx Server
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 and Magento community edition is installed via composer. 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. 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:
- 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.
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 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:
Here, we’re simply setting the $cors_origin to the current request origin if it matches either https://localhost:8000 or 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)