All You Need To Know About CORS

I’ll never forget the first time I stumbled across a Cross-Origin Resource Sharing (CORS) error. Trying to digest terms like the Same-origin policy, preflight requests, CORS policy, and Access-Control-Allow-Origin header all at once left me with more questions than answers.

Today, let’s dive into the world of CORS and why it’s the bane of developer’s existence. I promise it’ll only take 5 minutes to get a good grasp on it!

If you're just looking to resolve your CORS errors, skip right ahead to the solution!

What the heck is CORS?

So, what is CORS, and why does it keep insisting on ruining our day?!

Imagine this: you’re working on a fancy new website, let’s call it example.com. You’ve built a sleek frontend, and you’re ready to hook it up with your backend API from api.example.com.

You’ve tested the API a million times on Postman, and everything looks perfect. The response structure is flawless, and you’re feeling super confident. But then, you send a request from your webpage to the API, and bam — you're hit with the dreaded CORS error:

Access to fetch at 'https://api.example.com/posts' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Talk about a buzzkill!

Basically, CORS is a security mechanism that browsers enforce to prevent websites from making requests to domains different from the one serving the web page. Think of it like having a bouncer at a club who only lets in people from the same neighborhood (or domain, in this case).

💡
Wait a minute, aren't example.com and api.example.com the same domain?
Not quite! Even though they share the same domain name, they are considered different origins because they have different subdomains. In the world of CORS, the scheme/protocol, domain, and port must match exactly for two URLs to be considered the same origin.

See this determination rules table for the Same-origin policy.

When do CORS errors happen?

Here's the kicker: CORS errors only happen when you're making requests to APIs hosted on different domains from the browser.

If you're testing your API with Postman or Insomnia (or any other API development/testing tool), you'll never see this error because those tools don't give a damn about CORS rules.

Okay, what’s going on? Why is my browser doing this?

Well, your browser is just trying to protect you from potential vulnerabilities (e.g. CSRF attacks).

How? When your website makes a request to an API on a different domain, the browser sometimes automatically performs a series of steps to ensure the request is safe.

This process involves making a preflight request (which is an OPTIONS request) sent by the browser to the API server before the actual request is made.

Preflight Requests

Here's an example of preflight requests in the network tab:

Example of preflight requests in the network tab
Preflight requests include headers like Access-Control-Request-Method and Access-Control-Request-Headers to indicate the intended request method and headers

The preflight request (automatically issued by the browser) checks if the server allows the cross-origin request and includes the necessary CORS headers.

If the server responds with the appropriate CORS headers, the browser proceeds with the actual request. Otherwise, the request is blocked.

sequenceDiagram participant Browser participant API Browser->>Browser: Check if preflight request is needed Note right of Browser: Preflight request is needed for certain types of requests (e.g., a POST request with custom headers) Browser->>API: OPTIONS preflight request API-->>Browser: Server responds with CORS headers Browser->>Browser: Check if CORS is allowed Note right of Browser: If CORS is allowed, proceed with the actual request Browser->>API: Actual fetch("https:/api.example.com/posts") POST request API-->>Browser: Response

Wait, you said sometimes?

Preflight requests are only triggered for certain cross-origin requests.

Yep, you heard that right. The browser will only send a preflight request if the actual request meets any of the following conditions:

  • Only for certain types of requests, e.g. POST, PUT, DELETE
  • Your request includes custom headers (e.g., X-Custom-Header)
  • The Content-Type header has a value other than application/x-www-form-urlencodedmultipart/form-data, or text/plain. Commonly used ones are like application/json 👀

How to fix CORS errors

Alright, now that you understand what's actually going on, let's talk about how to fix your CORS mess. Here’s how you can typically go about it:

Scenario: You’re making an API request in the browser from example.com to api.example.com/posts and you ran into a CORS error.

flowchart TD Start([CORS Error Encountered]) --> OwnServer{Do you own/control the server?} OwnServer --Yes--> ImplementCORS[Implement CORS middleware] ImplementCORS --> AllowOrigin{Allow specific origin or *?} AllowOrigin --"Specific Origin"--> SetOrigin[Set Access-Control-Allow-Origin to your website's origin e.g. https://example.com] AllowOrigin --"All Origins"--> SetAsterisk[Set Access-Control-Allow-Origin to *] SetOrigin --> Success([CORS Issue Resolved]) SetAsterisk --> Success OwnServer --No--> Workaround{Consider workarounds} Workaround --Proxy--> ProxyRequest[Proxy requests through a server you control] Workaround --"Third-Party"--> ThirdPartyService[Use a third-party service that handles CORS] ProxyRequest --> Success ThirdPartyService --> Success
CORS errors should primarily be handled on the backend side, not client-side workarounds.

CORS Configuration

The first thing you should check is if you/your team owns or controls the API server (i.e. api.example.com in this case). If you do — awesome! Here's how you can make it happen:

  • To enable CORS on the server side, configure your API server to include the necessary CORS headers in its responses
  • This is typically done by setting up CORS middleware or configuring the server framework to handle CORS

What CORS response headers should I add?

For example, these headers are to be added to your API server's HTTP response to enable CORS for requests originating from https://example.com:

Header Description Must-Have Example Value
Access-Control-Allow-Origin Specifies the allowed origin. Yes https://example.com
Access-Control-Allow-Methods Specifies the allowed HTTP methods. Yes GET, POST, OPTIONS
Access-Control-Allow-Headers Specifies the allowed headers. Yes Content-Type, Authorization, X-Custom-Header
Access-Control-Allow-Credentials Indicates if credentials are allowed. No true
Access-Control-Max-Age Specifies the cache duration for preflight responses. No 86400 (24 hours)

Here is an example of how you might modify the code to add CORS headers to a response from a Node.js Express server:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());

app.post('/posts', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://example.com');
  res.setHeader('Access-Control-Allow-Methods', 'POST');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  // Send the actual response data here
  res.json({
    "userId": 1,
    "id": 1,
    "title": "My title",
    "body": "My post body"
  });
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

This is only for a simple demonstration. A more scalable/maintainable approach would be to use middleware to handle CORS globally

💡
Server-side frameworks and libraries often provide built-in CORS support or plugins to simplify CORS configuration. See the example of how to do it in Express.js.

Um… I don’t own the 3rd-party API server

If you don't control the API server, well, you do have a few choices.

Unless the API owners decide to open up their CORS policy, you'll have to find a workaround. Here are some example workarounds:

  1. Proxy Server (Recommended): you can proxy the requests through a proxy server (which you can self-host) e.g. CORS Anywhere
  2. CORS Browser Extensions: some browsers have extensions or plugins that allow you to bypass CORS restrictions during development. These extensions modify the browser's behavior by adding CORS headers to the requests. This should only be used for development and testing purposes

Key Takeaways

  1. Preflight requests are automatically issued by the browser (when conditions are met)
  2. CORS errors only happen with browser requests to different domains, not server-side applications.
  3. To fix CORS errors, implement CORS middleware on the server side
  4. If you lack control over the API server, consider solutions like proxying requests or using third-party services, but keep security in mind

Happy coding, and may your requests always find their way around the CORS maze!

Hosted on Digital Ocean.