bg-img

    Created by Maximilian Schwarzmüller
    Last Updated on May 31, 2020

    A typical CORS error message.

    Do you know this error message?

    “Access to fetch at ‘http://localhost:3000/messages’ from origin ‘http://localhost:8080’ has been blocked by CORS policy […]

    This is a CORS error.

    And this kind of error can be really challenging for newcomers to web development.

    So let’s explore and solve it.

    # What is CORS?

    CORS stands for Cross-Origin Resource Sharing.

    It’s a security concept built into modern browsers.

    And it has one simple goal: Resources (e.g. endpoints, data) exposed by a server should not be accessible by some random other server.

    The concept behind CORS is simple: A server by default does not expose its resources to other servers.

    Server A, by default, is not able to access resources exposed by Server B.

    Only frontend pages served by Server B will be able to access resources (e.g. API routes) exposed by Server B.

    So frontend and backend need to have the same origin - hence the name: Cross-Origin Resource Sharing.

    By default, sharing across different origins (= servers) is not allowed.

    # Solving CORS Errors

    Of course, in many modern web apps it’s quite normal to have different servers (= origins) for frontend and backend.

    So we want to enable cross-origin access.

    And doing so is simple: The backend server (!) needs to set appropriate headers on the response it sends back to the frontend. It needs to explictly allow that frontend to use the response.

    Browser check those response headers and block the client-app from using the response if such headers should not be set - and by default they aren’t.

    Which headers are these?

    Here are the three most important ones which you typically need:

    (this is a Node/ Express example, but the headers are the same, no matter which programming language you use)

    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
    • Access-Control-Allow-Origin: This header controls which other domains should be allowed to access the resources. This can be a wildcard (*) to allow all other origins to access the resources but you could also lock it down to specific domains.
    • Access-Control-Allow-Methods: This header controls which Http methods are supported - methods not specified here will yield to CORS errors.
    • Access-Control-Allow-Headers: This header controls which extra headers the client (!) may send with its request. If other headers are added, the request will lead to a CORS error response.

    You find all available CORS headers here.

    In the above example, any other server may send POST and GET requests. In addition to default headers, the Content-Type header will be allowed.

    So this client-side code would now work:

    const response = await fetch('http://localhost:3000/messages', {
      method: 'POST',
      body: JSON.stringify({ text: userMessage }),
      headers: {
        'Content-Type': 'application/json',
      },
    })

    This code would still fail for example:

    const response = await fetch('http://localhost:3000/messages', {
      method: 'PATCH', // unsupported Http method  body: JSON.stringify({ text: userMessage }),
      headers: {
        Authorization: 'Bearer TOKEN', // unsupported header  },
    })

    That’s really all that’s to CORS. Set the right backend response headers and you’re good!

    Of course that means that you can only enable CORS on backends “you own”. If you’re dealing with some public API which you haven’t written yourself, you won’t be able to get CORS to work in case you’re getting errors.

    Of course that would typically mean that this API does simply not want third-party requests. Or you’re using an incorrect URL.

    # Two Common Mistakes

    There are two common mistakes related to CORS:

    1. You’re getting a CORS error and you want to fix it by adding headers to the client-side request
    2. You think that CORS protects your API resources against unwanted access

    The first point is a mistake I see quite a bit out there.

    You get a CORS error, you google and read that it’s about some headers that need to be set and you go ahead and add those headers to the outgoing request you’re sending in your client-side app.

    const response = await fetch('http://localhost:3000/messages', {
      method: 'POST',
      body: JSON.stringify({ text: userMessage }),
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',    'Access-Control-Allow-Methods': 'POST, GET',    'Access-Control-Allow-Headers': 'Content-Type',  },
    })

    This will not work!

    As written and explained above: The CORS headers need to be added to the response sent by the backend, not to the request sent by the client!

    As mentioned earlier: If you don’t have access to the code behind an API that’s giving you a CORS error, you’ll have no way of fixing it. That API doesn’t want you to send requests - it’s as easy as that.

    The second point (“CORS protects an API against unwanted access”) also is wrong.

    Indeed, CORS is a concept that block a client-side app from using restricted resources.

    But that’s just the case because browsers care about CORS. It’s really just a browser concept.

    If you send a request to an API with some other tool (e.g. with Postman) you’ll have no problems getting that restricted data. Postman simply doesn’t care about CORS headers.

    So CORS is just a browser concept and not a strong security mechanism.

    It allows you to restrict which other web apps may use your backend resources but that’s all. Definitely better than nothing but not something you should use as a content-protection mechanism!