Skip to main content

CORS Headers

What is CORS?

Cross-Origin Resource Sharing (CORS) is a security feature implemented in browsers to control access to resources located outside of a given domain. It is a mechanism that allows or denies requests for resources from a web page served by one domain (the "origin") to a server at a different domain.

Why Is CORS Required?

Traditionally, web browsers implement a security model known as the Same-Origin Policy (SOP). SOP restricts web pages from making requests to a different domain than the one that served the web page. While this policy prevents malicious scripts from interacting with resources from another domain, it also limits legitimate cross-origin requests essential for modern web applications.

CORS was introduced as a solution to safely override the SOP under certain conditions, allowing controlled cross-origin requests, thus enabling functionalities like APIs, CDNs, and external libraries to work seamlessly across domains.

How CORS Works

When a web application makes a cross-origin HTTP request, the browser automatically adds an Origin header to the request, indicating the domain of the web page. The server then decides whether to accept or reject this request based on its CORS policy.

If the server allows the request, it responds with the appropriate CORS HTTP headers, such as Access-Control-Allow-Origin, indicating which origins are permitted. The browser then permits the web page to access the response if the server's response matches the request's origin.

During a CORS request,

  • the browser first sends a preflight request (OPTIONS method) with the Access-Control-Request-Method and Access-Control-Request-Headers (optional) to check if the server allows the actual request.
  • the server responds with the Access-Control-Allow-Origin, Access-Control-Allow-Credentials (optional), and Access-Control-Expose-Headers (optional) headers, indicating permissions and accessible data.
  • if the preflight is successful, the browser sends the actual request with the allowed method and headers.

CORS HTTP Headers

Example

Let's take a practical scenario. Here a client application hosted at https://example-client.com sends a cross-origin POST request to https://api.example-server.com with credentials and a custom header (X-Auth-Token).

Request Headers:

POST /resource HTTP/1.1
Host: api.example-server.com
Origin: https://example-client.com
Content-Type: application/json
X-Auth-Token: abc123

Server Response Headers:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example-client.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: X-Custom-Header
Content-Length: 123

In this example, the server permits the client from https://example-client.com to access the resource, allowing credentials to be sent and exposing the custom header X-Custom-Header.

CORS Request Headers

The client/browser sends the following headers to the server to identify the website initiating the API call.

Header NamePurposeExample Value
OriginIdentifies the origin of the request (originating website).https://example-client.com
Access-Control-Request-MethodInforms the server about the HTTP method intended for the actual request during a preflight request.POST
Access-Control-Request-HeadersLists custom request headers the browser plans to send with the actual request during a preflight request.Content-Type, X-Auth-Token

CORS Response Headers

The server responds to the original (or pre-flight) request with the following headers, specifying what is permitted. This allows the web-browser to determine whether to proceed with the cross-domain call.

Header NamePurposeExample Value
VaryIndicates headers that influence the response, potentially including Origin (relevant for CORS).Origin
Access-Control-Allow-OriginSpecifies which origins are permitted to access the resource.https://example-client.com
Access-Control-Allow-CredentialsSpecifies whether credentials (cookies, authorization headers) are allowed in cross-origin requests.true
Access-Control-Expose-HeadersLists custom response headers that should be accessible to client-side JavaScript.X-Custom-Header, Content-Length
Access-Control-Allow-MethodsSpecifies the HTTP methods that are permitted when accessing the resource in cross-origin requests.GET, POST, PUT, DELETE
Access-Control-Allow-HeadersLists the HTTP headers that can be used during the actual request.Content-Type, X-Auth-Token
Access-Control-Max-AgeSpecifies how long the results of a preflight request can be cached by the browser.3600
Timing-Allow-OriginSpecifies origins that are allowed to view timing information via the Resource Timing API.https://example-client.com

The Origin header (in the request) and Vary header (in the response) plays a role in CORS as well, though they are not strictly CORS-specific headers.

Server-Side Implementation of CORS

CORS is managed by the web server that provides the API. The server determines which origins or domains are permitted to send AJAX or XMLHttpRequest calls.

  1. Configure the server to include CORS headers, such as Access-Control-Allow-Origin, in its responses.
  2. Define the allowed origins, which can be a specific domain, multiple domains, or a wildcard (*) to permit all domains.
  3. Specify which HTTP methods (GET, POST, etc.) and headers are permitted.
  4. Handle preflight requests. Preflight requests use the OPTIONS method and are sent by the browser to determine if the actual request is safe to send.

Various frameworks in Java, JavaScript, Go, and Python offer built-in support or plugins for easily implementing CORS on the server side.

  • In Java, frameworks like Spring Boot and Jersey are prevalent. Spring Boot facilitates CORS through annotations such as @CrossOrigin and global configurations, while Jersey uses response filters to add CORS headers.
  • JavaScript (Node.js) frameworks such as Express.js, Hapi.js, and Koa.js each have their approaches. Express.js utilizes the cors middleware, Hapi.js offers plugins like hapi-cors-headers, and Koa.js supports CORS through third-party middleware.
  • For Go (Golang), frameworks like Gin and Echo provide in-built or middleware support for CORS. Gin includes built-in middleware for CORS configuration, whereas Echo uses a dedicated middleware for the same purpose.
  • In the Python ecosystem, Django and Flask are popular choices. Django's django-cors-headers middleware and Flask's Flask-CORS extension allow developers to control CORS headers with ease. FastAPI, a newer Python framework, also offers built-in support for CORS.

Implementing CORS on the Client-Side

CORS is primarily a server-side mechanism enforced by trusted web browsers to ensure security. Browsers adhere to CORS policies, while non-browser clients like Postman, Bruno, or custom code in Java, Python, or Node.js ignore CORS headers entirely. Here are some key points and best practices for client-side handling of CORS:

  1. Making Cross-Origin Requests:

    • Use JavaScript's XMLHttpRequest or the modern Fetch API for making cross-origin requests.
    • Ensure you specify the appropriate HTTP methods, headers, and credentials (if needed) in your requests.
  2. Handling CORS Errors:

    • CORS errors typically appear as JavaScript console errors indicating that the requested resource's origin is not permitted.

    • Implement proper error handling in your code to gracefully deal with these errors. For example:

      fetch('https://example.com/api', { method: 'GET' })
      .then(response => {
      if (!response.ok) throw new Error('CORS error or other issue');
      return response.json();
      })
      .catch(error => console.error('Error:', error));
  3. Proxy Server for Restricted APIs:

    • If the API server does not allow requests from your domain, set up an HTTP proxy on your server to route client requests to the target API.
    • This effectively bypasses CORS restrictions by making the request from your server, not the browser. Example:
      Client -> ProxyServer -> TargetAPI
  4. Understanding Non-Browser Clients:

    • Tools and libraries like Postman, Curl, and custom backend scripts do not enforce CORS because CORS is a browser-specific security mechanism. This means you can directly make requests from these clients without encountering CORS restrictions.
  5. Request Configuration Tips:

    • Use the mode: 'cors' option in Fetch API for CORS requests. For example:

      fetch('https://example.com/api', { 
      method: 'GET',
      mode: 'cors'
      });
    • Set credentials: 'include' if you need to send cookies or HTTP authentication headers with cross-origin requests.

Important Notes:

  • CORS is designed to protect end-users by enforcing origin restrictions in web browsers. If an API does not explicitly allow your origin, you cannot override these restrictions directly from the client side.
  • Always validate server responses to ensure that critical data is not exposed or misused, even when using a proxy.

Further Reading

  1. CORS at MDN
  2. Fetch - the specification