Categories
cors fetch-api javascript preflight

No ‘Access-Control-Allow-Origin’ header is present on the requested resource—when trying to get data from a REST API

995

I’m trying to fetch some data from the REST API of HP Alm. It works pretty well with a small curl script—I get my data.

Now doing that with JavaScript, fetch and ES6 (more or less) seems to be a bigger issue. I keep getting this error message:

Fetch API cannot load . Response to preflight request doesn’t
pass access control check: No ‘Access-Control-Allow-Origin’ header is
present on the requested resource. Origin ‘http://127.0.0.1:3000’ is
therefore not allowed access. The response had HTTP status code 501.
If an opaque response serves your needs, set the request’s mode to
‘no-cors’ to fetch the resource with CORS disabled.

I understand that this is because I am trying to fetch that data from within my localhost and the solution should be using Cross-Origin Resource Sharing (CORS). I thought I actually did that, but somehow it either ignores what I write in the header or the problem is something else.

So, is there an implementation issue? Am I doing it wrong? I can’t check the server logs unfortunately. I’m really a bit stuck here.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

I am using Chrome. I also tried using that Chrome CORS Plugin, but then I am getting another error message:

The value of the ‘Access-Control-Allow-Origin’ header in the response
must not be the wildcard ‘*’ when the request’s credentials mode is
‘include’. Origin ‘http://127.0.0.1:3000’ is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.

    1520

    This answer covers a lot of ground, so it’s divided into three parts:

    • How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems
    • How to avoid the CORS preflight
    • How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems

    How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems

    If you don’t control the server your frontend code is sending a request to, and the problem with the response from that server is just the lack of the necessary Access-Control-Allow-Origin header, you can still get things to work—by making the request through a CORS proxy.

    You can easily run your own proxy with code from https://github.com/Rob–W/cors-anywhere/.
    You can also easily deploy your own proxy to Heroku in just 2-3 minutes, with 5 commands:

    git clone https://github.com/Rob--W/cors-anywhere.git
    cd cors-anywhere/
    npm install
    heroku create
    git push heroku master
    

    After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/.

    Now, prefix your request URL with the URL for your proxy:

    https://cryptic-headland-94862.herokuapp.com/https://example.com
    

    Adding the proxy URL as a prefix causes the request to get made through your proxy, which:

    1. Forwards the request to https://example.com.
    2. Receives the response from https://example.com.
    3. Adds the Access-Control-Allow-Origin header to the response.
    4. Passes that response, with that added header, back to the requesting frontend code.

    The browser then allows the frontend code to access the response, because that response with the Access-Control-Allow-Origin response header is what the browser sees.

    This works even if the request is one that triggers browsers to do a CORS preflight OPTIONS request, because in that case, the proxy also sends the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight succeed.


    How to avoid the CORS preflight

    The code in the question triggers a CORS preflight—since it sends an Authorization header.

    https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

    Even without that, the Content-Type: application/json header will also trigger a preflight.

    What “preflight” means: before the browser tries the POST in the code in the question, it first sends an OPTIONS request to the server, to determine if the server is opting-in to receiving a cross-origin POST that has Authorization and Content-Type: application/json headers.

    It works pretty well with a small curl script – I get my data.

    To properly test with curl, you must emulate the preflight OPTIONS the browser sends:

    curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
        -H 'Access-Control-Request-Method: POST' \
        -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
        "https://the.sign_in.url"
    

    …with https://the.sign_in.url replaced by whatever your actual sign_in URL is.

    The response the browser needs from that OPTIONS request must have headers like this:

    Access-Control-Allow-Origin:  http://127.0.0.1:3000
    Access-Control-Allow-Methods: POST
    Access-Control-Allow-Headers: Content-Type, Authorization
    

    If the OPTIONS response doesn’t include those headers, the browser will stop right there and never attempt to send the POST request. Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it’s any other status code, the browser will stop right there.

    The server in the question responds to the OPTIONS request with a 501 status code, which apparently means it’s trying to indicate it doesn’t implement support for OPTIONS requests. Other servers typically respond with a 405 “Method not allowed” status code in this case.

    So you’ll never be able to make POST requests directly to that server from your frontend JavaScript code if the server responds to that OPTIONS request with a 405 or 501 or anything other than a 200 or 204 or if doesn’t respond with those necessary response headers.

    The way to avoid triggering a preflight for the case in the question would be:

    • if the server didn’t require an Authorization request header but instead, e.g., relied on authentication data embedded in the body of the POST request or as a query param
    • if the server didn’t require the POST body to have a Content-Type: application/json media type but instead accepted the POST body as application/x-www-form-urlencoded with a parameter named json (or whatever) whose value is the JSON data

    How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems

    I am getting another error message:

    The value of the ‘Access-Control-Allow-Origin’ header in the response
    must not be the wildcard ‘*’ when the request’s credentials mode is
    ‘include’. Origin ‘http://127.0.0.1:3000‘ is therefore not allowed
    access. The credentials mode of requests initiated by the
    XMLHttpRequest is controlled by the withCredentials attribute.

    For requests that have credentials, browsers won’t let your frontend JavaScript code access the response if the value of the Access-Control-Allow-Origin header is *. Instead the value in that case must exactly match your frontend code’s origin, http://127.0.0.1:3000.

    See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.

    If you control the server you’re sending the request to, a common way to deal with this case is to configure the server to take the value of the Origin request header, and echo/reflect that back into the value of the Access-Control-Allow-Origin response header; e.g., with nginx:

    add_header Access-Control-Allow-Origin $http_origin
    

    But that’s just an example; other (web) server systems have similar ways to echo origin values.


    I am using Chrome. I also tried using that Chrome CORS Plugin

    That Chrome CORS plugin apparently just simplemindedly injects an Access-Control-Allow-Origin: * header into the response the browser sees. If the plugin were smarter, what it would be doing is setting the value of that fake Access-Control-Allow-Origin response header to the actual origin of your frontend JavaScript code, http://127.0.0.1:3000.

    So avoid using that plugin, even for testing. It’s just a distraction. To test what responses you get from the server with no browser filtering them, you’re better off using curl -H as above.


    As far as the frontend JavaScript code for the fetch(…) request in the question:

    headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
    headers.append('Access-Control-Allow-Credentials', 'true');
    

    Remove those lines. The Access-Control-Allow-* headers are response headers. You never want to send them in requests. The only effect of that is to trigger a browser to do a preflight.

    5

    • 14

      Superb answer, my issue was the remote server not responding to OPTIONS requests, so after fiddling about with requests and headers for what seemed like ages I resolved it by removing the headers Content-Type and Access-Control-Allow-Origin – thank you!

      – Morvael

      Jan 29, 2019 at 9:50

    • 1

      Fantastic answer to a common problem. FWIW, we had server responding with 405 (Method not supported) when adding a ‘Authorization: Bearer’ header to a request, so the fix was to move the token to a POST field instead as control of the server response to the OPTIONS check was not possible in the time frame given by the client.

      Jul 17, 2020 at 5:45

    • 1

      Actually after upload the new version of extension to the Azure DevOps, you have to update rights for it in the dev.azure.com{your_organization}/_settings/extensions?tab=installed when you add new scopes “scopes”: [“vso.build”] in the manifest file.

      Aug 16, 2020 at 14:14

    • 3

      https://cors-anywhere.herokuapp.com/ is now no longer usable. The client will now receive a 403 Forbidden error – unless the developer explicitly requests to get temporarily passed. Here is the announcement: github.com/Rob–W/cors-anywhere/issues/301. I’d suggest to simply remove the cors-anywhere reference from the answer since it’s no longer useful.

      Feb 7, 2021 at 11:42

    • the part of “xxx-Credentials: true ” is updated. I think it is stopped to allow access CORS calls when -Origin is set matches the browser url but different to the HTTP call endpoint.

      Sep 2, 2021 at 11:39

    186

    This error occurs when the client URL and server URL don’t match, including the port number. In this case you need to enable your service for CORS which is cross origin resource sharing.

    If you are hosting a Spring REST service then you can find it in the blog post CORS support in Spring Framework.

    If you are hosting service using a Node.js server then

    1. Stop the Node.js server.
    2. npm install cors --save
    3. Add following lines to your server.js
    const cors=require("cors");
    const corsOptions ={
       origin:'*', 
       credentials:true,            //access-control-allow-credentials:true
       optionSuccessStatus:200,
    }
    
    app.use(cors(corsOptions)) // Use this after the variable declaration
    

    3

    • 10

      including the port number 🙁

      Mar 16, 2018 at 18:07

    • 1

      Note: you also need to remove your mode: 'no-cores' settings from your fetch() options on the client. Your JSON body won’t be sent unless mode: 'cores' in your request.

      Mar 14, 2021 at 3:25


    • 1

      This question is actually about making a request. So assume you don’t have access to the REST server? Is there no way to make a request?

      Sep 30, 2021 at 19:21

    129

    The problem arose because you added the following code as the request header in your front-end:

    headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
    headers.append('Access-Control-Allow-Credentials', 'true');
    

    Those headers belong to the response, not request. So remove them, including the line:

    headers.append('GET', 'POST', 'OPTIONS');
    

    Your request had 'Content-Type: application/json', hence triggered what is called CORS preflight. This caused the browser sent the request with the OPTIONS method. See CORS preflight for detailed information.

    Therefore in your back-end, you have to handle this preflighted request by returning the response headers which include:

    Access-Control-Allow-Origin : http://localhost:3000
    Access-Control-Allow-Credentials : true
    Access-Control-Allow-Methods : GET, POST, OPTIONS
    Access-Control-Allow-Headers : Origin, Content-Type, Accept
    

    Of course, the actual syntax depends on the programming language you use for your back-end.

    In your front-end, it should be like so:

    function performSignIn() {
        let headers = new Headers();
    
        headers.append('Content-Type', 'application/json');
        headers.append('Accept', 'application/json');
        headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
        headers.append('Origin','http://localhost:3000');
    
        fetch(sign_in, {
            mode: 'cors',
            credentials: 'include',
            method: 'POST',
            headers: headers
        })
        .then(response => response.json())
        .then(json => console.log(json))
        .catch(error => console.log('Authorization failed: ' + error.message));
    }
    

    3

    • 31

      This should be the top answer – I really dislike the idea of bypassing CORS, particularly by routing it through a third party.

      Jun 21, 2019 at 13:12

    • 1

      hey, whats ‘Header()’ please?

      – mitsu

      Dec 17, 2019 at 20:44

    • 2

      @mitsu If you refer to the line : let headers = new Headers(); above, then it is an interface of fetch API to do actions with http request or response headers. Visit developer.mozilla.org/en-US/docs/Web/API/Headers to get the details as well as examples on using it.

      – Lex Soft

      Jan 3, 2020 at 13:41