This article was originally published on the Educative blog
Modern web pages use more outside scripts and assets than ever before. By default, JavaScript follows the same-origin policy and can only make calls to URLs on the same domain as the running script. So, how can we get our JavaScript-powered pages to use outside scripts?
CORS is the answer.
Cross-origin resource sharing (CORS) is a mechanism that allows a way for web pages to access API or assets running on a different restricted domain.
Today, we'll explore CORS in-depth and learn how to activate it on different front-end frameworks.
Here’s what we’ll cover today:
- What is CORS?
- How does CORS work?
- Types of CORS requests
- Quick guide to implement CORS
- What to learn next
What is CORS?
Cross-origin resource sharing (CORS) is a browser mechanism that allows a web page to use assets and data from other pages or domains.
Most sites need to use resources and images to run their scripts. These embedded assets present a security risk as the assets could contain viruses or allow server access to a hacker.
Security policies mitigate the security risks of asset use. The policy rules what assets a requesting site can load based on origin or contents and regulates the amount of access given to the requesting site. Each policy must have enough restrictions to secure the web server but not enough to hurt functionality.
Same-origin is the most secure type of policy that prevents access to any outside server. All assets for a site must come from the same origin. Most of the time, same-origin is a good choice as most scripts can function with only local resources. However, sometimes we'll want to allow access to outside assets such as videos, live-streams, or pictures.
What is an origin?
Origin refers to 3 parts: a protocol, a host, and port number. Protocol refers to the application layer protocol, often HTTP. The host is the main site domain that all pages fall under, like Educative.io. Finally, the port number is the communication endpoint for the request, which defaults to port 80.
Many sites use a form of cross-origin policy called cross-origin resource sharing (CORS) that defines a way for a web page and the host server to interact and determine if it is safe for the server to allow access to the web page.
CORS is a middle ground policy between security and functionality as the server can approve certain outside requests without the insecurity of approving all requests.
Lived Example of CORS
The most prevalent example of CORS are advertisements on non-native sites.
For example, imagine you're watching a YouTube video and you see an Android advertisement. YouTube's servers are reserved for their essential resources and cannot locally store every possible advertisement.
Instead, all ads are stored on the advertisement company's servers. The advertisement company has allowed viewing access to YouTube to allow a YouTube web page to play the stored Android advertisement video.
The benefit of this system is that YouTube can use content from another server without using local storage. Also, it allows the advertisement company to roll out new advertisements quickly as they only need to update what ad is passed to YouTube from their server.
What assets can CORS request?
Sites use CORS requests to load:
- Fetch requests or HTTP requests like
XMLHTTPRequests
- Web-fonts and TrueType fonts only available for cross-site loading
- Web GL textures
- Images and videos
- CSS shapes
You can use CORS to freely embed these types of assets in your site and avoid the creation of local copies.
How does CORS work?
CORS adds new HTTP headers to the list of standard headers. The new CORS headers allow the local server to keep a list of allowed origins.
Any requests from these origins are granted and they're permitted to use restricted assets. The header to add to the acceptable origins list is Access-Control-Allow-Origin
.
There are many different types of response headers that enable different levels of access.
Here are a few more examples of CORS HTTP headers:
Access-Control-Allow-Credentials
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Request-Headers
Access-Control-Request-Method
Origin
When a web browser wants to access a site, it will send the site server a CORS GET
request. If granted, the GET
request will allow the browser to view the page, but nothing more.
Most servers allow GET
requests from any origin but will block other types of requests.
The server will either send back the wildcard value, *
, which means access to the requested data is unrestricted, or the server will check the list of allowed origins.
If the requester's origin is on the list, the web page is permitted to view the web page and the server echoes the name of the allowed origin.
If not, the server will return a declined message that states if the origin is disallowed from all access or if it is disallowed from the specific action.
Types of CORS requests
The GET
request above is the simplest form of request to allow viewing only. There are different types of requests that allow for more complex behavior like cross-origin requests for data manipulation or deletion.
These different requests exist because we may want to grant different levels of access depending on the origin. Perhaps we'd like all GET
requests to be granted but only our partnered advertising company can edit assets.
The separation of request types allows us to decide the exact clearance level of an origin and ensure each origin can only perform requests essential to its function.
Most requests fall into two major categories:
- Simple requests: These requests do not trigger a preflight check and use only "safelisted" CORS headers.
- Preflight requests: These requests send a "preflight" message that outlines what the requester would like to do before the original request. The requested server reviews this preflight message to ensure the request is safe to allow.
Simple requests
Simple requests do not require a preflight check and use one of three methods: GET
, POST
, and HEAD
. These requests are from before CORS was invented and therefore are allowed to skip to CORS preflight check.
GET
:
The GET
request asks to view a representation of the shared data file from a specific URL. It can also be used to trigger file downloads.
An example would be visiting any site on the web. As an outside user, we can only see the content of the site and cannot alter the text or visual elements.
GET /index.html
HEAD
:
The HEAD
request previews the headers that would be sent with a GET
request. It's used to sample what content exists at a specific URL without accessing it.
For example, you could HEAD
a download URL to receive its Content-Length
header. This would let you know the file size of the download before you agree to download it.
HEAD /index.html
POST
:
The POST
request asks to transmit data to the requested server, which could result in a change in the server. If a POST
request is triggered multiple times, it may have unexpected behavior.
An example of this is adding a comment to a forum thread.
The browser sends a request to add your input comment to the server. Once accepted, the forum server takes the newly received data (the comment) and stores it for others to view.
POST /test HTTP/1.1
Host: foo.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2
Preflight requests
Some methods generate an additional preflight request that is sent ahead of the original request. Preflight requests are automatically generated with the OPTIONS
method for functions that can affect user data or make a grand change in the server.
The OPTIONS
method is used to gather further information on how the requester is permitted to interact with the server. It returns what method options the requester is approved for.
OPTIONS
is a safe method, meaning it cannot change anything accessed. out as it'll be sent behind the scenes if you use a preflight method.
You'll not need to manually call the OPTIONS
method. Preflight requests are automatically issued from the browser when you attempt to request a method tagged as "to be preflighted".
The most common preflighted method is DELETE
that deletes the selected file or asset from the server.
The preflight request includes the requester's origin and the desired method, indicated using Access-Control-Request-Method
.
The server analyzes the preflight request to check if this origin has access to do such a method.
If yes, the server returns all methods the origin is permitted to use and indicates that you can send the original request.
If not, the original request is ignored.
The requester browser can then cache this preflight approval for as long as it is valid.
You can see the expiration date of the approval by checking the value of Access-Control-Max-Age
.
The requester browser can then cache this preflight approval for as long as it is valid. You can see the expiration date of the approval by checking the value of Access-Control-Max-Age
.
Quick Guide to implement CORS
To get started with CORS, you'll have to enable it on your apps. Below is a selection of code from different frameworks that will make your app CORS ready.
Nodejs Express App:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/', function(req, res, next) {
// Handle the get for this route
});
app.post('/', function(req, res, next) {
// Handle the post for this route
});
Flask:
Install the package:
$ pip install -U flask-cors
Then add it to your Flask app:
# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)
Apache:
Add the following line inside either <Directory>
, <Location>
, <Files>
or <VirtualHost>
sections of your server config.
Header set Access-Control-Allow-Origin "*"
To ensure changes are applied correctly, run apachectl -t
then reload your Apache using sudo service apache2 reload
.
Spring Boot Applications in Kotlin:
The following Kotlin code block enables CORS on Spring Boot applications.
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
@Component
class CorsFilter : WebFilter {
override fun filter(ctx: ServerWebExchange?, chain: WebFilterChain?): Mono<Void> {
if (ctx != null) {
ctx.response.headers.add("Access-Control-Allow-Origin", "*")
ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
if (ctx.request.method == HttpMethod.OPTIONS) {
ctx.response.headers.add("Access-Control-Max-Age", "1728000")
ctx.response.statusCode = HttpStatus.NO_CONTENT
return Mono.empty()
} else {
ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
return chain?.filter(ctx) ?: Mono.empty()
}
} else {
return chain?.filter(ctx) ?: Mono.empty()
}
}
}
Nginx:
The following code block enables CORS with preflight request support.
#
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
#
# Tell the client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
What to learn next
Congratulations on finishing your first steps toward CORS mastery. This powerful front-end tool is essential to be hired by top web development employers like Google and Amazon.
To continue your CORS learning journey, some next topics to check out are:
- Request authentication with credentials
- Ajax requests with CORS
- CORS in PHP
- Third-party cookies in CORS
To continue to widen your front-end skill-set, Educative has created the Become a Front-End Developer Path. This path includes tutorials and in-browser examples on writing and styling website front-ends. By the end, you'll have practiced several real-world projects and even launch your own personal website.
Happy learning!
Continue learning about front-end JavaScript
- Getting started with React and TypeScript
- An introduction to full stack JavaScript development
- What is Node.js? A beginner's introduction to JavaScript runtime
Start a discussion
What would you like to learn about front-end web development next? Was this article helpful? Let us know in the comments below!
Latest comments (0)