When using the WordPress REST API within a JavaScript application, you might encounter an error like this:

Access to XMLHttpRequest at 'https://wordpress.dev/wp-json/wp/v2/posts' from origin 'http://localapp.test' has been blocked by CORS policy: Request header field x-requested-with is not allowed by Access-Control-Allow-Headers in preflight response.

Most CORS issues can be solved by adding the following to your .htaccess file:

Header add Access-Control-Allow-Origin "*"

However, when you try the REST API request again from your application, you’ll get a new error.

'Access-Control-Allow-Origin' header contains multiple values 'http://localapp.test, *', but only one is allowed

But why? The .htaccess rule we added from above only has a single value. The reason for this is that the WordPress REST API is already setting CORS headers using the rest_send_cors_headers() function. You can override this by removing the existing CORS headers provided by WordPress and defining your own.

Add the following to your functions.php file:

function my_custom_rest_cors() {
  remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
  add_filter( 'rest_pre_serve_request', function( $value ) {
    header( 'Access-Control-Allow-Origin: *' );
    header( 'Access-Control-Allow-Methods: GET' );
    header( 'Access-Control-Allow-Credentials: true' );
    header( 'Access-Control-Expose-Headers: Link', false );

    return $value;
  } );
}
add_action( 'rest_api_init', 'my_custom_rest_cors', 15 );

There are lots of reasons why you wouldn’t setup the headers in the way shown above, but this is a great baseline for getting the REST API configured to play nice with Axios, jQuery.ajax() or the Fetch API.

Hope that helps!