Django is a Python web framework that allows rapid web application development. Apps developed in Django may need to interact with other applications hosted on different domains (or even just different ports). For these requests to succeed, you’ll need to use cross-origin resource sharing (CORS) in your server.
Luckily, in Django there’s already a module that’s easy to install and configure to allow CORS requests and avoid errors. So, if you want to know more about CORS and how to enable it in your Django server, be sure to keep reading.
What Is CORS?
CORS is a mechanism to allow interaction with resources hosted on different domains. For instance, one of the most common scenarios to apply it is with Ajax requests.
In order to illustrate how CORS works, let’s assume you have a web application that lives in domain.com. But, to save user information, the app calls an API hosted in another URL—for example, api.domain.com. So, when a request to save data is sent to api.domain.com, the server evaluates the requests based on its headers and the request’s source.
If you allow the URL domain.com in the server, it will provide the proper response. If the domain is not allowed, the server provides an error. This information exchange occurs using HTTP headers.
Errors Involving CORS
CORS is a security feature that web clients (browsers) implement that can make requests to a specific server to fail. Some possible server responses may include
- An unauthorized status (403)
- An error in a preflight request indicating which URLs can send CORS requests
As a clarification, a preflight request is a petition that browsers send to the server to discover what HTTP methods it accepts in requests. Then, the server can return an error status and a list of CORS-enabled URLs. If the server doesn’t include the domain making the request, the browser won’t even perform the actual data request.
As a rule of thumb, if you’re dealing with different domains, remember to be on the lookout for CORS issues. Also remember that using a different HTTP protocol or even a different port counts as a different domain. But there’s no need to worry, as current browsers’ tools are very helpful when diagnosing these issues.
Enabling CORS in Django
Since Django is a web framework, it’s very simple to enable CORS. So, here are the steps you must take to do so.
Install the CORS module:
python -m pip install django-cors-headers
Once that’s done, enable the module in Django. This is done in the installed apps section. Oh, and don’t forget the trailing comma; otherwise, you’ll get an error.
INSTALLED_APPS = [ ... 'corsheaders', ... ]
Next, add the middleware classes to listen in on server responses. Middleware classes hook on Django’s request/response processing. You can think of it as a plugin system to modify Django’s input or output.
MIDDLEWARE = [ ..., 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ..., ]
I’d recommend that you place the class
CorsMiddleware before any other middleware that can generate responses, such as
CommonMiddleware. This is because any other class may prevent the module from generating the appropriate CORS headers.
Finally, configure at least one of the required settings and any of the optional settings that you’d like to. Let’s review those settings and the purpose of each in the next sections.
Required settings tell the module how to evaluate a request’s origin. From there, the module decides, based on the settings you defined, if the origin is valid in order to continue processing the request and to provide a response.
You can set the module to allow requests from specific domains, regular expressions, or all requests. What options you should configure will depend on your back end’s purpose. Sometimes all origins are valid, but in other cases, you’ll need to narrow them to only a few, as shown below.
CORS_ALLOWED_ORIGINS is the list of origins authorized to make requests. For example, below I’ve specified four origins:
CORS_ALLOWED_ORIGINS = [ "https://domain.com", "https://api.domain.com", "http://localhost:8080", "http://127.0.0.1:9000" ]
CORS_ALLOWED_ORIGIN_REGEXES are regular expressions that match domains that can make requests. This setting is especially useful if you have many domains.
CORS_ALLOWED_ORIGIN_REGEXES = [ r"^https://\w+\.domain\.com$", ]
CORS_ALLOW_ALL_ORIGINS setting accepts only true or false. If true, the server will accept all requests. However, for security purposes, it’s better to use one of the above settings to limit valid request sources.
The optional parameters already have default values, which are valid in most situations. But if you need additional fine-grained permissions, these settings are the way to go. With them, you can restrict CORS responses according to URLs. Also, you can allow specific actions (
PUT, etc.), specific headers for requests, or even cookies. Let’s review the parameters.
CORS_URLS_REGEX setting restricts which URLs the server will send CORS headers to. It’s useful, for example, when you just want to send headers on part of your site. Here’s an example:
CORS_URLS_REGEX = r'^/api/.*$'
CORS_ALLOW_METHODS setting limits what methods are allowed for CORS. These are the default values:
CORS_ALLOW_METHODS = [ 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', ]
CORS_ALLOW_HEADERS is a list of non-standard headers allowed in the request. The default value is below:
CORS_ALLOW_HEADERS = [ 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', ]
CORS_EXPOSE_HEADERS is a list of headers exposed to the browser. The default is an empty array.
CORS_PREFLIGHT_MAX_AGE setting defines the time in seconds a browser can cache a header response to a preflight request. It defaults to 86,400 seconds (one day).
CORS_ALLOW_CREDENTIALS is a true or false value. So, its value determines whether the server allows cookies in the cross-site HTTP requests.
As you’ve seen in this post, CORS is a security feature designed to protect the user from malicious websites. In this case, the protection is to allow only specific domains to perform CORS requests. Thus, back-end servers require the proper configuration to accept such requests.
Nevertheless, this feature sometimes can get in the way during your project’s development process. To configure a development environment, you need to consider the security restrictions CORS requires. That makes it a bit tricky. But once you configure it correctly, you can forget all about it.
Still, don’t forget to disable it if all your requests will originate from the same domain once you deploy your app in production. Otherwise, make sure you configure it properly to avoid unexpected errors. As I explained above, for Django, this step is very easy to perform.
This post was written by Juan Pablo Macias Gonzalez. Juan is a computer systems engineer with experience in backend, frontend, databases and systems administration.