How to Enable Cross Origin Resource Sharing (CORS) on Django REST Framework

To enable cross origin resource sharing we need to enable some headers to authorize origin (browser ) to make requests to cross(different) origin. Browsers make preflight request to determine whether the given origin can make request to cross origin web servers respond to these preflighted requests made by browser to with typical response headers Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers and Access-Control-Allow-Credentials. With these headers server communicates with browsers whether it can make further request for the given (cross) origin

What is an origin ?

Origin
Origin Explained

As illustrated in the above figure origin means scheme, domain and port of the URL combined. For more information on cross origin resource sharing refer “Cross Origin Resource Sharing Explained

If you are writing REST (JSON) API and you want this API to be used by different origin for example API server is at api.example.com and website is being served at site.example.com .

To create CORS REST API you need to carefully setup or return proper headers in each request and it’s preflight request as explained in the “Cross Origin Resource Sharing Explained” . But, it would be tedious add these headers in each request (or view) as shown below

    def options(self, request):
        resp = Response()
        resp['Access-Control-Allow-Origin'] = '*'
        resp['Access-Control-Allow-Credentials'] = "true"
        resp['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
        resp['Access-Control-Allow-Headers'] = 'Access-Control-Allow-Headers, Origin, Accept, ' \
                                               'X-Requested-With, Content-Type, Access-Control-Request-Method,' \
                                               ' Access-Control-Request-Headers, credentials'

If whole API said to be shared with cross origins, then it is better to configure CORS globally as a middleware so that each response will be patched with CORS headers. A better well know library for this task to accomplish is “django-cors-headers”.

Following steps guide on how to install and configure djang-cors-headers to make your API cross origin recourse sharable

Install Django CORS Headers

pip install django-cors-headers

or

python -m pip install django-cors-headers

Add django-cors-headers to INSTALLED_APPS in project settings.py

INSTALLED_APPS = [ 
....
'corsheaders'
....
]

Typical INSTALLED_APPS including corsheaders would be

INSTALLED_APPS = [
    'api.apps.ApiConfig',
   # Custom Project App 

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'corsheaders'
]

Add CorsMiddleware to MIDDLEWARE_CLASSES in settings.py . Make sure it the first one in the list of MIDDLEWARE_CLASSES or make sure it before any middleware that generates response

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
     ....
]

Configure the CORS_ALLOWED_ORIGIN

CORS_ALLOWED_ORIGINS = [
    "https://site.example.com",
    "https://sub.example.com",

]

Assuming origin of document(site in browser) is site.exmaple.com. List of origins listed in the setting CORS_ALLOWED_ORIGINS are allowed to make CORS request. To explain it even better these (one of these) origins will be mentioned in the header ‘Access-Control-Allow-Origin’ of REST API request if it is originated from one of these origins so that browser knows it is allowed to make requests to cross origin (Ex: api.example.com) from origin (Ex: site.exmaple.com)

Other SETTINGS of django-cors-headers that can be configured in settings.py file are

Each setting of django-cors-headers is explained as follows

CORS_ALLOWED_ORIGINS
CORS_ALLOWED_ORIGIN_REGEXES
CORS_ALLOW_ALL_ORIGINS

CORS_URLS_REGEX
CORS_ALLOW_METHODS
CORS_ALLOW_HEADERS
CORS_EXPOSE_HEADERS
CORS_PREFLIGHT_MAX_AGE
CORS_ALLOW_CREDENTIALS

CORS_ALLOWED_ORIGINS
CSRF_TRUSTED_ORIGINS
CORS_REPLACE_HTTPS_REFERER

Django CORS Headers Explained

CORS_ALLOWED_ORIGINS

Type: Sequence of Strings. That is, list or tuple

A list of origins that are allowed to make cross site HTTP request. Requesting origin will be sent back to the client in the header “Access-Control-Allow-Origin” header. It is by default []

Example:

CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://subdomain.example.com",
    "http://localhost:8000",
    "http://127.0.0.1:9082",
]

CORS_ALLOWED_ORIGIN_REGEXES

A list of regular expression strings that match the origins that are allowed to make cross-site HTTP requests to the site. It is useful when you have large number of sub domains that you want allow all of them or fraction of them in such a case using CORS_ALLOWED_ORIGINS would be tedious to list all of them

Example:

CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://\w+\.example\.com$",
]

old alias: CORS_ORIGIN_REGEX_WHITELIST

CORS_ALLOW_ALL_ORIGINS

Type: Boolean

By default it is False. If True it will allow all origins to make cross-site HTTP requests by invalidating both CORS_ALLOWED_ORIGINS and CORS_ALLOWED_ORIGIN_REGEXES settings.

This is dangerous to allow all origins unless it is internal, since it allows all sites to request resource from your website


CORS_URLS_REGEX

Use this setting when you want to restrict cross origin requests to part of your site. For example you want allow cross site HTTP requests to resource or path following /api

CORS_URLS_REGEX = r"^/api/.*$"

CORS_ALLOW_METHODS

A list of HTTP request methods or verbs that are allowed in the request. If this setting is omitted it is default to

CORS_ALLOW_METHODS = [
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
]

default headers can be import as corsheaders.defaults.default_methods .

CORS_ALLOW_HEADERS

Headers that are allowed to be used in the actual request. If this setting is omitted. It is defaults to

CORS_ALLOW_HEADERS = [
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
]

Use CORS_ALLOW_HEADERS to list non-standard HTTP headers to be permitted in the cross site HTTP requests. This settings result in adding of mentioned headers in the setting in the actual HTTP header “Access-Control-Allow-Headers” in the HTTP response to the preflight request.

Default headers that are allowed can be import as follows ‘corsheaders.defaults.default_headers‘ .

CORS_EXPOSE_HEADERS

List of extra standard or non standard headers to be exposed to the browser such that they can be accessed using script. This settings sets the provided headers in the actual HTTP response header ‘Access-Control-Expose-Headers’. It is default to []

CORS_PREFLIGHT_MAX_AGE

Type: Int

Integer value that describes the number of seconds that browser can cache the preflight response still it is upto browser to honour the response header. Where, this settings sets the response header “Access-Control-Max-Age” which is sent in preflight response. if it is set to 0 or false “Access-Control-Max-Age” will not be sent in response. It’s default value is 86400 that is one day.

CORS_ALLOW_CREDENTIALS

Type: Boolean

This variable must be Boolean. It is default to False. Set it to True to allow client(browser) to access Cookies and to be included in cross-site HTTP requests. If this setting is true, Response header “Access-Control-Allow-Credentials” is set to true

The header Access-Control-Allow-Credentials would not work, it is resulted in error or ignored if Access-Control-Allow-Origin is set to *. Even though Access-Control-Allow-Credentials is set to true browsers ignore it if Access-Control-Allow-Origin is set to wildcard

Also, set the setting ‘SESSION_COOKIE_SAMESITE’ to None to disable the default security that restricts the session to same origin.

CSRF_TRUSTED_ORIGINS

Type: List of strings

A list of strings representing trusted origins to allow for unsafe requests.

CORS_REPLACE_HTTPS_REFERER

Type: Boolean

Default: False

If CORS_REPLACE_HTTPS_REFERER is set to true CorsMiddleware will change the Referrer header to appropriate one that will pass the Django CSRF checks

Because, for a secure unsafe request which doesn’t include the Origin header, the request must contain Referrer header which is same as the origin present in the Host header

When you are using both cors-headers and CSRF middleware. You should keep the CorsMiddleware first as follows

MIDDLEWARE_CLASSES = [
    ...,
    "corsheaders.middleware.CorsMiddleware",
    ...,
    "django.middleware.csrf.CsrfViewMiddleware",
    "corsheaders.middleware.CorsPostCsrfMiddleware",
    ...,
]
Default image
neotam
Naveen T aka neotam. Programming language agnostic, Software architect, Python expert, Networking & DevOps engineer & consultant with 7+ years of experience in creating serious web applications, real time event-driven non blocking applications and database driven applications ranging from small scale to enterprise grade. website
Leave a Reply