javascriptphpreactjscorsfetch-api

React + PHP API throws CORS preflight error


I am trying to call a PHP API running on localhost:8000 from a React app running on localhost:3000. After many tries I am still getting the error:

"CORS Preflight Did Not Succeed"

Sent from the React app:

Sent from the app

Sent from the devtools:

Sent from the devtools

My API has following headers:

if (@$_SERVER['HTTP_ORIGIN']) {
  header("Origin: http://localhost:8000");
  header("Access-Control-Allow-Origin: *");
  header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
  header('Access-Control-Max-Age: 1000');
  header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
} 

I call the API with fetch like this (but it somehow sends empty request body):

let inputData:object = {email, password}
fetch("http://localhost:8000/data/login", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify(inputData)
})
.then(response => {
  console.log(response)
})
.catch(error => {
  console.log(error)
})

The strange thing is that the requests are working normally when sent directly from the browser devtools (2nd screenshot) or API clients like Insomnia:

Sent from an API client


Solution

  • Problem

    Your first screenshot indicates that the response to the preflight request has status code 404. However, a necessary condition for CORS preflight to succeed is an ok status (i.e. a status in the range 2xx). See the relevant section (3.2.3) of the Fetch standard:

    A successful HTTP response, i.e., one where the server developer intends to share it, to a CORS request can use any status, as long as it includes the headers stated above with values matching up with the request.

    A successful HTTP response to a CORS-preflight request is similar, except it is restricted to an ok status, e.g., 200 or 204.

    (my emphasis)

    Solution

    Make sure your server responds with a 2xx status to preflight requests that are meant to succeed.


    Additional remarks

    1. Allowing the Origin header is never necessary, simply because it's set by the user agent. You can drop Origin from the value of the Access-Control-Allow-Headers response header.
    2. Why you're setting an Origin header in the response is unclear... Origin is a request header. You should be able to drop that header("Origin: http://localhost:8000"); line.
    3. Instead of "manually" implementing CORS (which is error-prone), you should consider using a proven CORS middleware.