Hi everyone! In this post I’ll show you a simple way to handle CORS-related problems that usually happen on local development environments. I’ve suffered whit this for too long, and the time to end it has come.
When developing a frontend system that consumes an API, testing services API via cURL or tools like Postman looks great: our methods return the desired responses or errors, and everything works beautifully. But when we finally use our app interface to call the services through a browser (Chrome, Firefox, etc), we can face CORS-related errors, such as:
Access to XMLHttpRequest at 'https://yourapp.api.com' from origin 'http://localhost:xxxx' 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.
So, what’s CORS?
CORS (Cross-origin resource sharing) is a security mechanism implemented by browsers that uses additional HTTP headers to manage permissions to an application regarding wether it can consume resources (data, files, etc) from another app that has a different origin (domain, port or protocol).
Access-Control-Allow-Origin header indicates the allowed origins that can consume the services provided by the requested resource. This header could return with the
* value, indicating that any application can consume this service, or with a single domain.
We already have a “simple” solution to this problem: enabling CORS directly in your app (Node, Golang, Python (Flask), .NET Core, etc). However, this strategy is not so “simple”, because it needs an action from the backend developer to manually change the source code and go through all the steps of your pipeline (commit, CI/CD builds, publishing and others), which can take a long time and make a delay in your app delivery.
There's also some browser plugins that can be used as a trick to this problem, but honestly, I don’t use any of them as I’m trying to keep my Google Chrome as clean as possible (RAM is not for free, you know).
Of course, you’ll need Docker in your machine, but nowadays it’s almost impossible to talk about development without talking about Docker (at least to me 😃). So, go ahead and install it in your machine.
First, what is Docker? What is NGINX? What is a reverse proxy? Ok, slow down and let’s do it step by step:
- Docker, in a simple explanation, is an open source platform to run and manage isolated environments (containers). In other words, we can package our app in an isolated environment with portability and security to run everywhere.
- NGINX is a lightweight web server that can also act like a reverse proxy, email proxy or load balancer.
- A reverse proxy is a type of proxy server that retrieves information from one or more servers, being able to do any processing needed to the response and return it to the client. In this scenario, the resource achieve some anonymity and security.
We have only two files to solve our CORS problem. Starting with the Dockerfile definition, we have:
Here we are building a new Docker image using some instructions inside a Dockerfile. Wait, image? Dockerfile? Yeah, take a look:
- An image in Docker is a read-only template with a set of instructions that enables you to run a container. So, defining instructions, we create a base environment.
- Dockerfile is the file that contains these instructions.
Basically, whit this Dockerfile we are following these steps:
A. Creating an image using NGINX alpine as a base image
B. Setting up the working directory
C. Copying the conf file to the image
D. Hint with the EXPOSE instruction that NGINX is bound to the port 80 of the container
E. Running the command to start NGINX
Okay, let’s go ahead. With your Dockerfile in hands, run the command below to build the Docker image:
docker build -t cors .
We are asking the Docker to create an image, tag (-t) this image with the name cors and use the actual context (.).
Well, with the built image, we can look inside the NGINX conf file to understand the magic:
The trick is on line 25. We are inserting the header
Access-Control-Allow-Origin with the value
* in the request, indicating that any client could make the request. So, when the browser receive the response, it will understand that the source is accessible and go ahead with the request response.
Other two points to consider:
- I’ve added as a plus some instructions to allow other header fields, like Authorization, Cache-Control and Access-Control-Allow-Methods.
- If your client makes some request to investigate the methods allowed to this service and the allowed origin, I’ve also added this information to the OPTIONS request.
That’s it - now, you can go ahead and use your API to it’s fullest, be happy!
If you prefer, you can use DockerCompose to achieve the same result, but with one command instead of two 😃. Take a look at Github to see how and more details about the code.
Wait, but what’s DockerCompose? In the next post, I'll talk about it 😉.