Understanding Fetch API and CORS in JavaScript
This article provides a comprehensive overview of the modern JavaScript Fetch API, explaining its core architecture and how it executes HTTP network requests. It specifically examines how the browser security model interacts with the Fetch API, detailing the mechanisms behind Cross-Origin Resource Sharing (CORS), preflight checks, request modes, and the configuration required to securely exchange data across different origins.
What is the Fetch API?
The Fetch API is a native JavaScript interface designed for accessing
and manipulating HTTP pipeline components, such as requests and
responses. Introduced as a modern replacement for
XMLHttpRequest, Fetch is built around JavaScript Promises,
offering a cleaner, more readable syntax for asynchronous network
operations.
A basic fetch request looks like this:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));Understanding the Same-Origin Policy and Cross-Origin Requests
Browsers enforce the Same-Origin Policy (SOP) to protect user data from malicious scripts. Two URLs share the same origin only if their protocol, domain (host), and port match exactly.
A request is considered cross-origin when a web page
hosted on one origin (e.g., https://frontend.com) requests
resources from a different origin (e.g.,
https://api.backend.com). By default, SOP blocks scripts
from reading responses sent from another origin unless the server
explicitly grants permission via Cross-Origin Resource Sharing
(CORS) headers.
How the Fetch API Handles Cross-Origin Requests
The Fetch API handles cross-origin requests by integrating directly
with the browser’s CORS mechanism. When a cross-origin request is
triggered, the browser appends an Origin HTTP header
indicating where the request initiated.
Origin: https://frontend.com
The target server must respond with appropriate CORS headers to authorize access. The most critical header is:
Access-Control-Allow-Origin: https://frontend.com
(or * to allow any domain, though wildcards cannot
be used when sending user credentials).
Fetch Request Modes
The Fetch API accepts a mode configuration property that
defines how cross-origin requests are treated:
cors(Default): Enforces CORS validation. The browser expects valid CORS headers from the server. If the headers are missing or invalid, the request fails with a network error.no-cors: Limits the request to simple actions (likeGETorPOSTwith simple headers). JavaScript cannot access the response body or status code (the response type becomesopaque). This is primarily used for caching assets via Service Workers.same-origin: Rejects any request directed to a different origin immediately, preventing outgoing cross-origin network traffic.
Example specifying the mode:
fetch('https://api.backend.com/data', {
method: 'GET',
mode: 'cors'
});Simple Requests vs. Preflighted Requests
The Fetch API distinguishes between two types of cross-origin requests:
1. Simple Requests
A request is treated as “simple” if: - It uses methods like
GET, HEAD, or POST. - It only
sets safe headers (e.g., Accept,
Accept-Language, Content-Language). - The
Content-Type is limited to
application/x-www-form-urlencoded,
multipart/form-data, or text/plain.
These requests are dispatched immediately with the
Origin header attached.
2. Preflighted Requests
If a fetch request uses custom headers (e.g.,
Authorization), HTTP methods other than standard ones
(e.g., PUT, DELETE, PATCH), or a
Content-Type of application/json, the browser
automatically sends an OPTIONS preflight request
first.
The preflight request asks the server for permission before sending the actual request:
OPTIONS /data HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
The server must reply with corresponding approval headers:
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Once confirmed, the browser executes the actual fetch call.
Handling Credentials in Cross-Origin Fetch
By default, cross-origin fetch requests do not send or receive HTTP
cookies, HTTP authentication, or client-side SSL certificates. To
include credentials, set the credentials property:
fetch('https://api.backend.com/user-profile', {
method: 'GET',
credentials: 'include' // Options: 'omit', 'same-origin', 'include'
});When credentials: 'include' is used: - The server must
respond with Access-Control-Allow-Credentials: true. - The
server cannot use
Access-Control-Allow-Origin: *; it must explicitly specify
the requesting origin.