Open In App

HTTP Access Control (CORS)

Last Updated : 18 Feb, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Modern web applications often need to fetch data from different domains, known as a cross-origin request. For example, a site hosted on 'example.com' might need resources from 'api.example.com'. However, browsers enforce the Same-Origin Policy (SOP), which blocks requests to different domains for security, preventing issues like cross-site request forgery (CSRF).

To allow safe communication between different domains, Cross-Origin Resource Sharing (CORS) is used. CORS enables developers to specify which domains are permitted to access resources, bypassing SOP restrictions when necessary.

What is CORS?

CORS (Cross-Origin Resource Sharing) is a security feature implemented by web browsers that allows or denies requests for resources from one domain to be made from a different domain. It is a mechanism that allows web servers to specify which domains are permitted to access the resources they serve.

In simple terms, CORS defines how a browser should behave when making requests for resources (e.g., API calls, images, scripts) to a server from a different origin. Without CORS, browsers block these cross-origin requests to maintain security.

Why is CORS Important?

Without CORS, a malicious website could make API calls on behalf of a user and steal their sensitive data (e.g., making requests to a banking API from an unauthorized site). CORS helps prevent such attacks by restricting cross-origin requests unless explicitly allowed by the server.

Example of a CORS Issue

Imagine your front end is hosted at:

 https://frontend-example.com

And your API is hosted at:

https://api-example.com

If your frontend tries to fetch data from the API using:

fetch('https://api-example.com/data')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.log(error));

The browser blocks the request and throws a CORS error unless the API explicitly allows it.

What Requests Use CORS?

HTTP requests to another origin (domain, protocol, or port) use CORS. These types of requests include:

  • Simple requests: These are requests via GET, POST, or HEAD methods. Credentials included are things like HTTP headers and or cookies but not custom headers. The browser will handle simple requests automatically without a preflight check.
  • Non-simple requests: However, requests that use methods other than GET, POST, or HEAD (ie. PUT, DELETE), has custom headers, or sent credentials (eg cookies or tokens), all need to be checked further. Preworks by checking with the server with another request: the OPTIONS method.
  • Cross-origin resource requests with credentials: The server must explicitly allow an application when an application must send or receive cookies, authentication headers, or other credentials from a different origin.

How CORS Works?

To allow cross-origin requests, the server needs to send CORS headers in the response.

  • Browser sends request: The browser attaches an Origin header to a request when you make a request from one origin to another.

Example (http):

GET /api/data HTTP/1.1
Origin: https://frontend.com
  • Server evaluates request: The server checks this Origin header on the request and allows or denies on the set of CORS policies.
  • Server response: The server responds with appropriate CORS headers, e. g. Access-Control-Allow-Origin, if the server allows the origin requests, otherwise it denies the origin by returning an error (403 for example) response.
  • Example (HTTP): HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.com
  • Browser enforcement: If the CORS headers are legal, the browser will accept request & display result. A request is blocked (browser blocked with 403 error) and the resource isn't reachable if the headers are missing or if they’re not correct.

Header

Description

Access-Control-Allow-Origin

Specifies which domains can access the resource. Example: * (allows all origins) or https://example.com

Access-Control-Allow-Methods

Defines allowed HTTP methods (GET, POST, PUT, DELETE, OPTIONS).

Access-Control-Allow-Headers

Specifies which headers the client can send.

Access-Control-Allow-Credentials

Allows credentials like cookies and authentication tokens.

Access-Control-Allow-Origin (ACAO) HTTP Response Header

Without setting the Access-Control-Allow-Origin header, then any origin will be allowed to access the resource. This header can either:

  • Specify a single domain: It lets only the given domain access the resource. Example (http):
Access-Control-Allow-Origin: https://frontend.com
  • Use a wildcard (*): Any domain is then allowed to access the resource. In a way, this is convenient, but also insecure, if we don't use it properly.
Access-Control-Allow-Origin: *

It is not a good idea to use wildcard when the sensitive data or credentials are used.

Access-Control-Allow-Origin (ACAO) HTTP Request Header

A cross origin request includes the Origin header when a web browser sends a request. It will tell the server from which domain the request came from. According to which information it chooses to allow or deny access to this resource.

Example Request (http):

GET /api/data HTTP/1.1
Origin: https://frontend.com

Implementing Simple CORS

CORS is not too difficult to implement for simple GET or POST requests.

Now let us understand with the example.

Installation: First you need to install cors in your application.

npm install express cors
HTML
<!-- index.html -->
<html>
<head></head>
<body>
    <h1>Check the Browser Console for Output</h1>
    
    <script>
        // Make a fetch request to the API
        fetch('http://localhost:3000/data')
            .then(response => response.json())  // Parse JSON response
            .then(data => console.log(data))  // Log the data to console
            .catch(error => console.log('Error:', error));  // Catch any error
    </script>
</body>
</html>
JavaScript
// app.js
const express = require('express');
const cors = require('cors');  // Import the cors package
const app = express();

// Enable CORS for all origins (you can restrict it to specific domains later)
app.use(cors());

// Example route to get data
app.get('/data', (req, res) => {
    res.json({
        message: 'CORS is enabled for all origins!',
        data: [1, 2, 3, 4, 5]
    });
});

// Start the server
app.listen(3000, () => {
    console.log('Server running on http://localhost:3000');
});

Now, if we will comment out the app.use(cors()), then it will throw the error.

Output

Screenshot-2025-02-18-160437
CORS Error

When we use the app.use(cors())

Screenshot-2025-02-18-161649
HTTP Access Control (CORS)

In this example

  • app.use(cors()) allows all origins (or specific ones) to make requests to the server.
  • /data: Sends a JSON response to the client.
  • You can configure other routes as needed for your API.
  • Use app.listen(3000) to start the server on http://localhost:3000, and handle incoming requests.

Handling Preflight Requests

Some requests (e.g., PUT, DELETE, requests with custom headers) trigger a preflight request before the actual request.

What is a Preflight Request?

A preflight request is an OPTIONS request sent by the browser to check if CORS is allowed before making the actual request.

Example of a Preflight Request

OPTIONS /api/data HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

The server responds with (http):

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

Handling Preflight in Express.js

app.options('/data', cors()); // Enable CORS for preflight requests

Common CORS Errors and Fixes

Error

Cause

Solution

CORS policy: No 'Access-Control-Allow-Origin' header

Server did not send CORS headers

Add Access-Control-Allow-Origin in response headers

CORS policy: Method not allowed

Server does not allow the HTTP method

Add Access-Control-Allow-Methods in headers

CORS policy: Credential not allowed

Request uses credentials, but server does not allow them

Set Access-Control-Allow-Credentials: true

Does CORS Protect Against CSRF?

CORS is not intended to circumvent Cross Site Request Forgery (CSRF) attacks. CSRF attacks are attacks where a user’s authenticated session is exploited to force them to make another unwanted request to another website. Cross domain restrictions (CORS) only apply to the interactions between domains, so CSRF is not prevented.

In order to mitigate CSRF you should further increase your security measures such as CSRF tokens or check if the request’s Origin and Referer headers are set.

Vulnerabilities in CORS

Incorrect CORS configurations can put your application in danger of threats. Here are some common vulnerabilities:

  • Wildcard in Access-Control-Allow-Origin: However, using the * allows all the origins to access to the sensitive resources.
  • Misconfigured credentials: But allowing credentials (cookies, authentication headers) with wildcard origins opens a gaping security hole.
  • Improper use of preflight requests: Sometimes if the server handles preflight requests incorrectly it can grant restricted resources to unauthorized domains.

Best Practices for Managing CORS

  • Restrict Origins: Avoid using *, instead allow specific trusted domains.
  • Allow Only Necessary Methods: Limit Access-Control-Allow-Methods to only required HTTP methods.
  • Use Middleware: In Express, use the Cors package instead of manually setting headers.
  • Secure API Credentials: Avoid sending sensitive data in public CORS requests.
  • Enable Preflight Requests: Handle OPTIONS requests properly to avoid request failures.

Next Article

Similar Reads