Add CORS to REST API

Feature Card

Description

For the “Direct to Frontity” installation Frontity uses an external domain. The REST API doesn’t have CORS headers. We can create a PHP plugin for that.

User Stories

As a Frontity user
I want to add easily CORS headers to the REST API
so that I can use a different domain

Possible solution

add_action( 'rest_api_init', 'wp_rest_allow_all_cors', 15 );

function wp_rest_allow_all_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: POST, GET, OPTIONS, PUT, DELETE' );
    header( 'Access-Control-Allow-Credentials: true' );
    return $value;
  });
}

I’m not sure this is necessary. It seems that WP does set headers for CORS (I tried one clean installation with Nginx in production and it worked just fine).

Also, in the rest_pre_serve_request hook there is already a function registered called rest_send_cors_headers that sets the headers:

function rest_send_cors_headers( $value ) {
    $origin = get_http_origin();
 
    if ( $origin ) {
        // Requests from file:// and data: URLs send "Origin: null"
        if ( 'null' !== $origin ) {
            $origin = esc_url_raw( $origin );
        }
        header( 'Access-Control-Allow-Origin: ' . $origin );
        header( 'Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE' );
        header( 'Access-Control-Allow-Credentials: true' );
        header( 'Vary: Origin', false );
    } elseif ( ! headers_sent() && 'GET' === $_SERVER['REQUEST_METHOD'] && ! is_user_logged_in() ) {
        header( 'Vary: Origin', false );
    }
 
    return $value;
}

You might not see them when calling the REST API directly from the browser or from an app like Postman because the Origin header is not populated therefore the CORS headers are not set. But if you try setting the Origin header in Postman you’ll see that the CORS headers will be set on the response.

You are absolutely right, it does. Many thanks @orballo :heart:

@SantosGuillamot what do we do with this topic? I wouldn’t like to close it because I don’t want to limit the conversation. Do you have any idea?

I think it’s the same case as the one discussed here:

I’ve just discovered that this is a really bad approach, because if your cache of the REST API response includes headers and doesn’t overwrite them with a general CORS header ("*") then it will return the previous URL. This means that, if you are using that REST API for local development, it can cache Access-Control-Allow-Origin: localhost:3000, and your production site will stop working.

The thing is that, the code that WordPress is using right now is as “safe” as using "*" because it will copy the domain you pass in the Origin header. So, I guess it’s better to set it directly to "*" and avoid caching issues.

EDIT: Moving this back to Awaiting Development.

2 Likes

In the meantime, if someone wants to solve this type of CORS problem, they can use this plugin

or add this code in their WordPress:

add_action( 'rest_api_init', 'wp_rest_allow_all_cors', 15 );

function wp_rest_allow_all_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: POST, GET, OPTIONS, PUT, DELETE' );
    header( 'Access-Control-Allow-Credentials: true' );
    return $value;
  });
}