I found many discussions online about this, e.g. such as this great accepted answer, but still can't work out what's wrong. I've done the following:
- Created a React app using create-react-app.
- Started the server using npm startand can see my app in the browser.
- Using fetchto contact my local server:
    fetch("http://xxx.xxx.xxx.xxx/endpoint.php", {
      method: "post",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: credentials.username,
        password: credentials.password
      })
    }).then(response => {
      console.log("fetch competed!");
    });
- In my PHP endpoint,
<?php
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding");
    header("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS, DELETE");
    ...
- But then Chrome console debug says:
Access to fetch at 'http://localhost:3000/endpoint.php' from origin 'http://xxx.xxx.xxx.xxx:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.
Having done some reading, the preflight request sends an OPTIONS to the server, which I have allowed in the PHP header. I also tried some proxies in the package.json file and restarted the server using npm start.
I also tried passing through https://cors-anywhere.herokuapp.com/ and I still get an error:
Access to fetch at 'http://xxx.xxx.xxx.xxx:3000/php/login.php' (redirected from 'https://cors-anywhere.herokuapp.com/http://xxx.xxx.xxx.xxx:3000/php/login.php') from origin 'http://xxx.xxx.xxx.xxx:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.
I also tried replacing my fetch with something simpler:
    fetch(url, {
      method: "POST",
      body: new URLSearchParams("email=test@example.com&password=pw")
    });
but still it does not work.
I don't understand what the problem could be?