Hello! In our last lesson, we explored the evolution of the HTTP protocol, focusing on how versions like HTTP/2 and HTTP/3 introduced sophisticated mechanisms like multiplexing to boost performance. This evolution was driven by the changing architecture of web applications, which are often split between a user-facing frontend and a data-providing backend API.
This split architecture, where your frontend might live on https://app.mycompany.com while your API lives on https://api.mycompany.com, introduces a fundamental security challenge that every backend developer must master.
Today's Goal: This lesson will explain what Cross-Origin Resource Sharing (CORS) is. We will start from the root cause—the browser's Same-Origin Policy—to understand why CORS exists, then break down the HTTP header-based mechanism to see how it works, and finally discuss how to configure it correctly on your backend server.
The "Why": Understanding the Same-Origin Policy
Before we can understand CORS, we must first understand the security rule it's designed to relax: the Same-Origin Policy (SOP). This is a critical security mechanism built into every modern web browser.
Let's start by reading a clear definition of what constitutes an "origin" and the security risks that led to the creation of the SOP.
Deep dive in CORS: History, how it works, and best practices
The article 'Deep dive in CORS: History, how it works, and best practices' provides an excellent starting point. It defines what an origin is and uses simple but powerful examples to illustrate the dangers that the Same-Origin Policy protects against.
Please read the sections titled 'Origins & cross-origin', 'The many dangers of cross-origin requests', and 'Same-origin policy'. As you read, focus on these key questions: What three components define an 'origin'? What is the core security risk of allowing a script from a malicious website to make requests to another site where you are authenticated (like your bank)? What does the Same-Origin Policy fundamentally block?
As the article explained, the Same-Origin Policy is your browser's first line of defense against a whole class of web attacks.
To summarize:
- An origin is defined by the combination of scheme (e.g.,
http), hostname (e.g.,www.example.com), and port (e.g.,80). If any of these differ, it's a different origin. - The Problem: Without the SOP, a script running on
evil-site.comcould make afetch()request toyour-bank.com/api/transfer. If you were logged into your bank, your browser would automatically attach your session cookies to that request, potentially allowing the malicious script to initiate a transaction on your behalf. - The Solution (SOP): The browser enforces a simple rule: a script loaded from one origin is prohibited from reading the response of a request made to a different origin.
The SOP is very restrictive, and for good reason. But what happens when you intentionally build your application with a separate frontend and backend origin? You need a way to tell the browser, "It's okay, I trust this cross-origin request." That is precisely what CORS is for.
The "What": Cross-Origin Resource Sharing (CORS)
CORS is not a security feature that protects your server. Instead, CORS is a mechanism that allows a server to tell a browser that it is safe to allow a cross-origin request. The enforcement happens entirely within the browser.
This mechanism works through a standardized exchange of HTTP headers. The following short video gives a great high-level overview.
🔓 Mastering CORS: How to Securely Enable Cross-Origin Requests
The video 'Mastering CORS' quickly explains the basic interaction between the browser, a frontend application, and a backend API during a cross-origin request.
Watch from the beginning to 02:44. This segment introduces the core headers (Origin and Access-Control-Allow-Origin) and distinguishes between 'simple' and 'preflight' requests.
Now that we have the general idea, let's dive into the technical specifics of how this header exchange works. CORS divides requests into two categories: simple requests and preflighted requests.
The "How" Part 1: Simple Requests
A "simple request" is one that is considered low-risk because it resembles the kind of requests that could be made by old HTML <form> tags, which predate these security policies.
Let's turn to the MDN Web Docs, the definitive reference for web standards, for a precise definition of what qualifies as a simple request.
Cross-Origin Resource Sharing (CORS) - HTTP | MDN Web Docs
The MDN page on CORS is an essential resource. We'll use it here to get the formal criteria for a simple request.
Read the section titled 'Simple requests'. Pay close attention to the three conditions: the allowed methods, the allowed headers, and the allowed Content-Type values.
So, if a request meets these criteria (e.g., a GET request with no custom headers), the flow is straightforward:
- The browser sends the
GETrequest to the cross-origin server, automatically adding anOriginheader (e.g.,Origin: https://app.mycompany.com). - The server checks the
Originheader against its list of allowed origins. - If the origin is permitted, the server processes the request and includes the header
Access-Control-Allow-Origin: https://app.mycompany.comin its response. - The browser receives the response, sees the matching
Access-Control-Allow-Originheader, and allows your JavaScript code to access the response data. If the header is missing or doesn't match, the browser blocks the response and logs a CORS error to the console.
Now, let's see this in action. The following video demonstrates this exact process by building a simple Python web server from scratch. This should resonate with your background and goal of understanding things from the root.
Cross Origin Resource Sharing (Explained by Example)
In his video 'Cross Origin Resource Sharing (Explained by Example)', Hussein Nasser provides a fantastic hands-on demonstration of CORS errors and how to fix them on a Python server.
Watch from 03:49 to 10:09. The presenter first shows a cross-origin fetch request failing. Then, he modifies his Python server code to add the Access-Control-Allow-Origin header, first with a specific origin and then with the wildcard (*), to resolve the error.
The "How" Part 2: Preflighted Requests
What happens if your request doesn't meet the "simple" criteria? For example, what if you want to send a DELETE request, or a POST request with a JSON body (Content-Type: application/json), or include an Authorization header with a token?
For these more "complex" or potentially destructive requests, the browser takes an extra safety step: it sends a preflight request.
A preflight is a preliminary check, using the OPTIONS HTTP method, that the browser sends before the actual request. It's like asking for permission: "Hey server, I'm about to send a DELETE request from this origin with a custom Authorization header. Is that okay with you?"
This two-step process is crucial for security.

Let's go back to the MDN documentation for a formal breakdown of the preflight mechanism and the headers involved.
Cross-Origin Resource Sharing (CORS) - HTTP | MDN Web Docs
We'll return to the MDN CORS page to understand the headers used in the preflight conversation.
Read the section 'Preflighted requests'. Focus on the new headers introduced in the OPTIONS request (Access-Control-Request-Method, Access-Control-Request-Headers) and the corresponding headers in the server's response (Access-Control-Allow-Methods, Access-Control-Allow-Headers).
The preflight flow looks like this:
- Preflight Request (
OPTIONS): The browser sends anOPTIONSrequest with:Origin: https://app.mycompany.comAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: Content-Type, Authorization
- Preflight Response: The server, if configured to allow this, responds with
204 No Contentand headers like:Access-Control-Allow-Origin: https://app.mycompany.comAccess-Control-Allow-Methods: GET, POST, OPTIONSAccess-Control-Allow-Headers: Content-Type, AuthorizationAccess-Control-Max-Age: 86400(caches the preflight result for 24 hours)
- Actual Request: If the preflight response was permissive, the browser then sends the actual
POSTrequest with the JSON body andAuthorizationheader. The server's response to this request must also includeAccess-Control-Allow-Originfor the browser to expose the response data.
Let's see the practical side of this. We'll finish the video from Hussein Nasser, where he implements the OPTIONS handler in his Python server to fix a preflight error.
Cross Origin Resource Sharing (Explained by Example)
This final segment of the video is the most relevant part for a backend developer, as it shows exactly how to handle a preflight OPTIONS request on the server.
Watch from 10:09 to the end. The presenter triggers a preflight request by adding a Content-Type: application/json header. He shows the initial failure, implements a basic do_OPTIONS method in his Python server, and then iteratively adds the necessary Access-Control-Allow-* headers to satisfy the browser's preflight check.
Configuring CORS in a Django Backend
Building a server from scratch is a great way to learn the fundamentals. In a real-world Django application, you won't be handling these headers manually in every view. Instead, you'll use a middleware that intercepts all requests and responses to apply your CORS rules globally.
The most popular library for this is django-cors-headers. It works by adding a middleware to your Django settings that checks incoming requests and adds the correct CORS headers to outgoing responses based on your configuration.
Common settings include:
CORS_ALLOWED_ORIGINS: A list of origins that are authorized to make cross-site HTTP requests. This is the most secure and recommended setting.- Example:
CORS_ALLOWED_ORIGINS = ["https://app.mycompany.com", "https://admin.mycompany.com"]
- Example:
CORS_ALLOW_ALL_ORIGINS: If set toTrue, this will allow all origins (Access-Control-Allow-Origin: *). This is acceptable for public APIs that don't use credentials like cookies, but it's insecure otherwise.CORS_ALLOW_METHODS: A list of HTTP verbs that are allowed, e.g.,['DELETE', 'GET', 'POST', 'PUT'].CORS_ALLOW_HEADERS: A list of non-standard HTTP headers that are allowed, likeauthorization.CORS_ALLOW_CREDENTIALS: If set toTrue, this allows cookies to be included in cross-origin requests. Crucially, if you enable this, you cannot setCORS_ALLOW_ALL_ORIGINStoTrue. You must explicitly list the allowed origins. This is a strict browser security rule.
For a deeper look into these configurations and other best practices, the following reading is highly recommended.
Deep dive in CORS: History, how it works, and best practices
Let's revisit the 'Deep dive in CORS' article to review best practices for configuring CORS policies.
Please read the sections 'Fine-tuning CORS' and 'Some best practices'. These sections cover the implications of using the wildcard (*), handling credentialed requests, and other practical considerations.
Conclusion
CORS is often a source of frustration for new developers, but as we've seen, it's a logical and necessary part of the web's security model. By understanding its foundation in the Same-Origin Policy, you can work with the browser's security features, not against them.
Key Takeaways:
- The Same-Origin Policy (SOP) is a browser security feature that blocks scripts from reading responses from a different origin.
- CORS is the mechanism that servers use to tell browsers it's safe to relax the SOP for specific origins, methods, and headers.
- CORS operates through a system of HTTP headers, primarily
Originfrom the client andAccess-Control-Allow-*from the server. - Simple requests (like
GET) are checked when the response arrives. - Preflighted requests (like
PUTor requests with custom headers) require a preliminaryOPTIONSrequest to get permission from the server before the actual request is sent. - As a backend developer, you will configure CORS via middleware, defining a clear policy of which origins are trusted.
Preview of the Next Lesson:
In this module, we've dissected the anatomy of HTTP, from its structure and methods to its evolution and security policies like CORS. The final piece of the puzzle is to learn how to observe and debug this traffic directly. In the next lesson, we will get hands-on and learn how to inspect and debug HTTP traffic using curl and browser developer tools, putting all the theory from this module into practice.