Simplified Overview of Cross-Origin Resource Sharing

Table of contents

Introduction
Cross-Origin Resource Sharing (CORS) is simply a method or mechanism for integrating applications. This mechanism enables controlled access to resources or information located outside a specific domain. So, clients’ web applications in domain A can interact with clients’ web applications in domain B. It’s that simple! In this article, Ill discuss why Cross-Origin Resource Sharing is important, give a brief history of its ancestor the Same-Origin Policy, talk about how CORS works, and give a few best practices to adopt when carrying out CORS Let’s get started, shall we?
Why is CORS Important For Modern Web Applications?
If you’re an avid internet user like me, you may have experienced instances where your browser pulls photos or videos from a photo platform API or gets sounds from a sound platform API. Actions like that are why CORS is important. CORS is a mechanism that lets the client browser confirm the authorization of the request with third parties before transferring any data.
What is the Same-origin Policy?
I cannot emphasize the importance of CORS without giving a brief history of its precursor, the Same-Origin Policy. Developers followed the Same-Origin Policy at an earlier stage of the Internet. It enforced the rule that clients can only send requests to a resource that has the same origin as the client’s URL. The port, protocol, and hostname of the client’s URL must match the server it requests from. Here is an example to understand this concept: Let’s say you host your website at:
https://myreader.com:443
This is your origin, which includes:
The Protocol: https
The Hostname:
myreader.com
The Port:
443
Now, according to the Same-Origin Policy, your JavaScript code running on https://myreader.com
can only make requests to a server with the exact origin — that means it can fetch data from:
https://myreader.com:443/api/data
But if it tries to request something from:
http://myreader.com
(different protocol),https://api.myreader.com
(different sub-domain),https://myreader.com:8443
(different port) or,https://anotherdomain.com
(different hostname).
…it will be blocked by the browser because of the Same-Origin Resource Sharing restrictions. The policy protects users from malicious scripts intending to steal data from other sites via their browser.
What can we understand from these examples?
Although the Same-Origin policy was very secure, it was too restrictive, and more approaches had to be developed. Developers needed an approach that was both secure and flexible, which led to the development of Cross-Origin Resource Sharing. So, how is Cross-Origin Resource Sharing the perfect approach?
For starters, CORS allows for different components hosted on different domains to communicate with each other. So web applications that need third-party features can access these API securely. That provides the flexibility that Same-Origin lacks.
CORS allows different components of an application that are hosted in seperate domains to communicate seamlessly.
Emphasizing again the flexibility that CORS provides, CORS reduces restrictions that come with the Same-origin Policy, allowing developers to have a list of domains that can access their resources, while maintaining high security.
CORS is flexible enough that developers can create responsive web applications with more features and functionality while maintaining seamlessness.
How Does CORS Work?
We understand that CORS is a mechanism that lets the client browser confirm the authorization of the request with third parties before transferring any data, but how does that happen? To understand how CORS works, we’ll have to learn about its components. The major components involved in CORS are the Origin, the Browser (Client-side), the preflight request, the CORS headers (Server-side response headers), the server, and the actual request.
The Origin
The origin is a combination of the scheme (protocol), host (domain), and port. An example of an origin is
https://myreader.com:645
.CORS gets triggered whenever a web page tries to access a resource from a different origin than its own.
Browser (Client-side)
This is the web browser where the JavaScript runs. It enforces CORS rules based on the server’s response.
When a script in the browser makes a cross-origin HTTP request (like
fetch()
orXMLHttpRequest
), the browser checks if it's allowed.For "non-simple" requests (e.g., methods like
PUT
, or headers likeAuthorization
), the browser first asks for permission by sending a preflight request using theOPTIONS
method.
Preflight Request
The preflight is an automatic OPTIONS
request sent by the browser before the actual request. It asks the server for permission to proceed with the real request. This is especially important for requests that are not considered "simple," such as those using methods like PATCH
or carrying custom headers. The preflight ensures that the server explicitly permits the cross-origin interaction.
CORS Headers (Server-Side Response Headers)
The server responds to preflight and actual requests with specific headers that control access. These include:
Access-Control-Allow-Origin
: It specifies which origins are allowed.Access-Control-Allow-Methods
: Lists the HTTP methods permitted.Access-Control-Allow-Headers
: It lists the request headers that can be used.Access-Control-Allow-Credentials
: It indicates whether credentials, such as cookies, are allowed.Access-Control-Expose-Headers
: It specifies headers exposed to the clients.Access-Control-Max-Age
: It defines how long the browser can cache the preflight response.
These headers form the backbone of CORS enforcement, and they must be configured properly to avoid errors.Server
The server evaluates the preflight request, checks the origin, method, and headers, and responds accordingly. It can choose to approve or deny access based on its CORS policy. A misconfigured server, such as one missing CORS headers, can unintentionally block legitimate requests or introduce security vulnerabilities if it’s too permissive.
Actual Request
If the preflight request is approved (or wasn’t needed), the browser sends the actual request (e.g., GET
, POST
, PUT
). The server must again respond with the correct CORS headers. If the browser doesn’t see the expected headers in the response, it will block access to the response data, even if the request itself succeeded.
To reiterate:
Let’s say a web page at https://clientsite.com
wants to send a DELETE
request to https://api.example.com
.
Because it’s a cross-origin request with a non-simple method (DELETE
), the browser steps in and automatically sends a preflight OPTIONS
request to https://api.example.com
.
This preflight request includes the origin and asks: “Am I allowed to make this kind of request?”
The server receives the preflight and responds with CORS headers like:
pgsqlCopy codeAccess-Control-Allow-Origin: https://clientsite.com
Access-Control-Allow-Methods: DELETE, GET
Access-Control-Allow-Headers: Authorization
The browser checks this response. If the origin and method are allowed, it proceeds to send the actual DELETE request.
The server then processes that request and includes the correct CORS headers again in its response.
The browser sees the Access-Control-Allow-Origin
header and allows the JavaScript code to access the data.
But if the server skips or misconfigures those headers, the browser blocks access, even if the server responded successfully. All you get is a CORS error in the browser console, and your code can’t use the response.
Best Practices for CORS
You should practice the following when carrying out Cross-Origin Resource Sharing on your server:
Use The Right HTTP Headers
By using the right headers, you can comply with CORS browser policies and avoid errors. The right headers to use are: Access-control-allow-origin
, Access-control-allow-methods
, and Access-control-allow-headers
.
Appropriately Define Your Access List
Have a comma-separated list to grant access to individual domains. Unless you want to make the API public, don’t use a wildcard because it may incur or create vulnerabilities. For instance, imagine you use a regular expression to allow any site ending in trusted-site.org
. This would give access to app.trusted-site.org
and blog.trusted-site.org
as intended.
However, it could also accidentally allow access to a harmful domain like phishingtrusted-site.org
, which is not actually part of your trusted group but still matches the pattern.
Instead of using a loose regular expression that matches any domain ending in trusted-site.org
, it’s safer to list only the exact subdomains you trust. For example, you can check if the Origin matches either https://app.trusted-site.org
or https://blog.trusted-site.org
and allow access only to those. This way, you avoid unintentionally granting access to malicious domains like faketrusted-site.org
, which may appear similar but are not part of your trusted network.
Correct Configuration of the API Server
Set up your API server to include important CORS headers in its responses.
Set up back-end logic to create the correct CORS headers based on the request’s origin.
With the right CORS headers, you can configure the server to handle
OPTIONS
requests before the main request is made.
Secure Your CORS Setup
To keep your app safe, avoid using *
(wildcard) in CORS. Do this instead:
a. Allow only trusted domains to access your resources.
b. Check and clean all inputs to prevent malicious requests.
c. Review your CORS settings often** to catch mistakes or security gaps.
d. Use HTTPS to protect data in transit.
e. Add server-side protections, like:
Rate limiting
Checking the origin
Token-based authentication
If you’re using Node.js, use a CORS library (like the CORS npm
package) to make setup easier and safer.
Set Up a Proxy Server to Handle CORS
You can use a proxy server to forward requests between the browser and your API. It helps bypass CORS issues by making the request appear to be from the same origin. Here’s how you do it:
a. Pick a place to run your proxy, such as your computer or a cloud server.
b. Install and set up proxy software (e.g., NGINX, Node.js with http-proxy).
c. Configure the proxy to change request headers as needed.
d. Add security rules such as login or IP restrictions.
e. Test it to make sure it forwards requests properly.
f. Keep it updated and monitored for performance and safety.
Avoid Using the Null Value in Your List
In some cases, such as file requests or requests from localhost
, browsers may set the Origin
header to null
. However, you should avoid allowing null in your list of permitted origins, as doing so can pose security risks by potentially granting access to untrusted or unauthorized sources.
Conclusion
Cross-Origin Resource Sharing (CORS) may seem like just another technical concept, but it's a crucial part of how the modern web works. It bridges the gap between strict browser security (thanks to the Same-Origin Policy) and the flexibility that today’s dynamic, interconnected apps demand.
Think of CORS as a gatekeeper; it doesn’t open the door wide for everyone, but checks credentials at the door, asking: “Who are you, and should you get in?” This balance between security and functionality makes CORS so valuable. Without it, your favorite apps wouldn’t be able to pull data from different services or integrate cool features from third-party APIs.
Whether you're a backend developer configuring CORS headers or a frontend developer debugging CORS errors in the browser console, understanding how this mechanism works will save you time, frustration, and security headaches. So, as you build web applications, always remember: CORS is not just about permissions, it’s about building trust between different domains, safely and deliberately.
See you on the next one, dev! ✨💋🚀
Subscribe to my newsletter
Read articles from Yoma Daniel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by