Cross Origin Resource Sharing (CORS)
Certain "cross-domain" requests, notably Ajax requests, are forbidden by default by the same-origin security policy.
Cross-Origin Resource Sharing is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
CORS allows for more freedom and functionality than purely same-origin requests and is more secure than simply allowing all cross-origin requests.
Setup for demo
In order to demo Cors, I am going to be a little creative and use a Django and a Flask web application. Note that all the code that we will write below is also present in this GitHub repository.
Django app
Create a new folder django-app. Install Django, ideally after creating a virtual environment and activating it.
pip install django
Now let's create a new Django project django_cors
.
django-admin startproject django_cors .
Let's create a new app called csrf_demo
.
python manage.py startapp cors_app
And add it to the INSTALLED_APPS
in sample_django_app/settings.py file:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Local
"cors_app.apps.CorsDemoConfig" # newly added
]
We will be using Django REST Framework for this demo. Let's install it:
pip install djangorestframework
And then add it to INSTALLED_APPS
as well:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# 3rd party
"rest_framework", # newly added
# Local
"cors_app.apps.CorsDemoConfig"
]
We will be keeping file changes to a minimum since the objective of this article is to only demo cors. Adding paths to both the project URL and app URL:
django_cors/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("cors_app.urls"))
]
cors_app/urls.py
from django.urls import path
from cors_demo.views import HomeView
urlpatterns = [
path("", HomeView.as_view(), name="home")
]
Now we will define the view:
cors_app/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class HomeView(APIView):
def get(self, request):
return Response("Hello from Django!")
That's it. Now let's create the Flask application.
Flask
Create a new folder flask-app. Inside, place the following files:
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("home.html")
templates/cors.html:
<!DOCTYPE html>
<head>
<!-- Prevent the Automatic Favicon Request -->
<link rel="icon" href="data:,">
</head>
<body>
<h1 id="cors-result">
Awaiting response...
</h1>
<script>
(async () => {
//AJAX call to Django API
let response = await fetch("http://localhost:8000")
let text = await response.text()
document.querySelector("#cors-result").innerHTML = text
})();
</script>
</body>
As you can see, we are making a cross-origin request to the Django local server in the cors.html template.
Demo
Use the migrate command to initialize the local database and run the Django application.
python django-app/manage.py migrate
python django-app/manage.py runserver
Run the Flask application.
flask --app flask_demo/app run --debug
If you are in VS code you can split the terminal and do this:
By default, the Django app runs on port 8000 and Flask on 5000.
Open your browser and keep the developer tools open. First, navigate to localhost:8000
Go to the network tab on developer tools:
Notice that there is no Access-Control-Allow-Origin
in the response headers
Now, navigate to localhost:5000
And if you check the console in developer tools you will see the following CORS error:
The error says that the request has been blocked by CORS policy since 'Access-Control-Allow-Origin' header is not present on the requested resource.
To fix this we will use a middleware, in the Django application, that will automatically include the appropriate HTTP headers based on our settings. Let's install django-cors-headers
which is the package recommended by Django REST Framework.
pip install django-cors-headers
Now update the django_cors/settings.py file in three places as indicated by the comment # newly added
:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# 3rd Party
"rest_framework",
"corsheaders", # newly added
# Local
"cors_app.apps.CorsAppConfig"
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # newly added
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
# newly added
CORS_ALLOWED_ORIGINS = (
"http://localhost:5000",
"http://localhost:8000"
)
Reload the page:
There you go - you have your Flask web page showing the response from the Django API! Also, check out the response headers:
As you can see, the header Access-Control-Allow-Origin: http://localhost:5000
has been added to the response.
Summary
This article explains the concept of CORS. Then, it demonstrates CORS by creating two separate web applications and making an AJAX request from one application to the other. As mentioned above, you can also find the code we have discussed in this article on GitHub.
Thanks for reading and I hope you found this article useful.