Internet Engineering

Cross-Origin Resource Sharing

Introduction · Policy · Usage · Example · Error · Middleware · Proxy

Fall 2026 · Amirkabir University of Technology
@1995parham

What is CORS?

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

What is an Origin?

The page https://domain-a.com/index.html has the origin scheme https, host domain-a.com, port 443; the path is not part of it. A fetch to https://domain-a.com/data.json is same-origin. http://domain-a.com (scheme), https://api.domain-a.com (host), https://domain-a.com:8443 (port) and https://domain-b.com (host) are cross-origin.

CORS also relies on a mechanism by which browsers make a preflight request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.

Preflight

A fetch with method PUT and Content-Type application/json. The browser first sends OPTIONS /data.json with Origin, Access-Control-Request-Method: PUT and Access-Control-Request-Headers: content-type. The server answers 204 No Content with Access-Control-Allow-Origin, Access-Control-Allow-Methods: GET, PUT, Access-Control-Allow-Headers: Content-Type and Access-Control-Max-Age: 600. Then the browser sends the real PUT with Origin and Content-Type: application/json, and the server answers 200 OK with Access-Control-Allow-Origin.

CORS Policy

The Cross-Origin Resource Sharing standard works by adding new HTTP headers that let servers describe which origins are permitted to read that information from a web browser.

Usage

For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. The Fetch API (and the older XMLHttpRequest) follow the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from unless the response from other origins includes the right CORS headers.

Example

An example of a cross-origin request, the front-end JavaScript code served from https://domain-a.com uses fetch to make a request for https://domain-b.com/data.json.

A page from https://domain-a.com runs fetch. A GET to the web server at domain-a.com is same-origin and always readable. A GET to the web server at domain-b.com is cross-origin and readable only if the response carries Access-Control-Allow-Origin.

Another Example

For example, suppose web content at https://foo.example wishes to invoke content on domain https://bar.other. Code of this sort might be used in JavaScript deployed on foo.example:


const url = "https://bar.other/resources/public-data/";

const response = await fetch(url);
const data = await response.json();
        
        

This operation performs a simple exchange between the client and the server, using CORS headers to handle the privileges:

The browser, on a page from https://foo.example, sends GET /resources/public-data/ to https://bar.other with the header Origin: https://foo.example, which the browser adds. The server answers 200 OK with Access-Control-Allow-Origin: *, which allows any origin, so the browser hands the response to the script.

Here is what Chrome actually sent to the server in this case (User-Agent and the sec-ch-ua client hints left out):


GET /resources/public-data/ HTTP/1.1
Host: bar.other
Connection: keep-alive
Accept: */*
Origin: https://foo.example
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://foo.example/
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
        
        

The request header of note is Origin, which the browser adds to show that the invocation is coming from https://foo.example. Now let's see how the server responds:


HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Wed, 14 Oct 2026 04:28:57 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

{"users":42}
        
        

The server answers with Access-Control-Allow-Origin: *, which means that the resource can be accessed by any origin.

CORS Error

What people call a CORS error is not really CORS at all. The restriction is the same-origin policy, which the browser applies by default; CORS is the mechanism a server uses to relax it.

So the message means: the server did not opt in.

Your script calls fetch for private.json. The browser sends GET /private.json with Origin: http://localhost:8081 to the server, and the server's handler runs. The server answers 200 OK with the body {"secret":42} and no Access-Control-Allow-Origin. The browser blocks it, and the script gets TypeError: Failed to fetch without seeing the status or the body. The console shows: Access to fetch at 'http://localhost:8082/private.json' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The request did reach the server; it is the response the browser keeps from your code.

Fixing CORS Error

When you are developing an application, you can fix CORS error in Front-end or Back-end.

Proxy Server (Frontend)

When you are developing a Front-end application, you often cannot change the responses of the server you are calling. Instead, let the development server act as a proxy: the page sends its request to the server it came from, so the browser sees a same-origin request. The proxy forwards it to the back-end server to server, where the same-origin policy does not apply, and hands the answer back as its own response. No CORS is involved at any step.

Flow

Inside the browser, a page from localhost:5173 calls fetch("/api/users"). The request goes to the Vite dev server on localhost:5173, the same origin, so there is no preflight and no Origin header. The dev server forwards /api/users to the back-end on localhost:8080, server to server, where the same-origin policy does not apply. The back-end has no CORS headers configured.

Example

With Create React App (now legacy, but you will meet it) the setting lives in package.json:

            
{
  "name": "application",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:8080"
}
            
        

With Vite the same setting lives in vite.config.js:

            
export default defineConfig({
  server: {
    proxy: {
      "/api": "http://localhost:8080",
    },
  },
});
            
        

Why does it work?

The browser only ever talks to the development server, so from its point of view every request is same-origin and no preflight is sent at all. The development server then forwards the request to the back-end from outside the browser, where the same-origin policy does not apply, and returns the answer as its own response.

Be careful

This is a development-time workaround only. In production the development server is gone, so the back-end must either send the right CORS headers or be served from the same origin as the front-end.

CORS Middleware (Backend)

When you are developing a back-end service, you add the Access-Control-Allow-* headers to your responses — you cannot set them on the request, the browser owns that side. We usually do it with a middleware that every response passes through.

Example

In Fiber framework of Golang, there is middleware called CORS. We use it in our application instance so all requests will be CORS request.

            
import (
  "github.com/gofiber/fiber/v3"
  "github.com/gofiber/fiber/v3/middleware/cors"
)

func NewApp() *fiber.App {
    // create a new app
    app := fiber.New()

    // Default config
    app.Use(cors.New())

    return app
}
            
        

See the full documentation of Fiber CORS middleware .

References 📚

Fork me on GitHub