Daryll Doyle WordPress Development and Consultancy in Cornwall

CORS with the WP REST API and IE11

C

A bit of a background, I’ve recently been working on a reasonably large, headless, multilingual WordPress build. The frontend and the backend needed to be hosted on separate domains, so we decided to build this site using the following technologies:

Backend:

Frontend:

All was going well until we were told late on in the project that we needed to support IE11. After polyfilling React and any custom libraries we’d introduced for the build, there was one real issue remaining, our ACF options pages weren’t translating in IE.

To talk you through this, I’ll need to rewind a little.

ACF Options Pages with WPML

ACF in all it’s greatness, doesn’t work amazingly with WPML. Posts, pages, media etc. is all fine, but if you use their options pages, they don’t get translated correctly (probably to do with them not being a post type).

To counter this, I found a little hack I could use when registering the page. When calling the acf_add_options_page() function to register a new options page, you can pass through an array of arguments, one of these being post_id. The post_id is what the options page will be saved under in the wp_options table.

I realised that if I adjusted this ID to append the current language to the post_id field, I’d be able to query options by language. As such, I wrote the following piece of code to register the pages using WPML.

if ( function_exists( 'acf_add_options_page' ) ) {

	// Default page title
    $options = array(
        'page_title' => 'Message',
    );
    
    // If WPML is active, append the language code to the post_id
    if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
        $options['post_id'] = sprintf( 'options_%s', ICL_LANGUAGE_CODE );
    }
    
    // Register the options page
    acf_add_options_page( $options );
}

I then had a custom WP REST endpoint set up to query these options. The method that did most of the work, looks something like this:

**
 * The callback for the `wp/v2/acf/options` endpoint
 * 
 * @param WP_REST_Request 	$request 	The WP_REST_Request object
 *
 * @return array|string 	The single requested option, or all options 
 *
 * @see ACFtoWPAPI::addACFOptionRouteV2()
 *
 * @since 1.3.0
 */
function addACFOptionRouteV2cb( WP_REST_Request $request ) {
    $options_string = 'options';
    $wpml_lang      = $request->get_header( 'X-WP-Wpml-Language' );

    if ( $wpml_lang ) {
        $options_string = sprintf( 'options_%s', $wpml_lang );
    }

    if ( $request['option'] ) {
        return get_field( $request['option'], $options_string );
    }

    return get_fields( $options_string );
}

As you can see, the method is using a custom HTTP header called X-WP-Wpml-Language to get the current language from the React side.

Allowing Custom Headers through CORS Preflight

To allow our custom header, we need to OK it with the REST API so that CORS Preflights let React know that it’s an allowed header.

After reading around about how to do this, I came across a really great post by Josh Pollock, which you can read here.

Josh tells us that we can use the rest_api_init action and the rest_pre_serve_request filter to adjust headers sent over the REST API. Therefore I had this code written up:

add_action( 'rest_api_init', function () {
    add_action( 'rest_pre_serve_request', function () {
        header( 'Access-Control-Allow-Headers: X-WP-Wpml-Language' );
    } );
}, 15 );

This code would allow our new X-WP-Wpml-Language header through, and it did. It worked fine in every browser, but then IE11 came along.

IE11 and HTTP Headers

So, lets dig into IE11 and see what’s the issue.

The REST API already sets some Access-Control-Allow-Headers headers in it’s core code. We’ve then sent another header through with more Access-Control-Allow-Headers headers. So the browser sees something like this:

Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Headers: X-WP-Wpml-Language

Sending headers like this should be fine and indeed all other browsers handle it correctly. but apparently IE11 can’t read headers split over multiple lines and therefore was only ever reading the first set (See here, here and here).

Don’t be fooled though, if you look in the IE11 developer tools, it will show you all the headers you sent on the same line as below, this makes it really confusing to debug.

Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Wpml-Language

So, to fix the CORS Preflight in IE11, we need to send all the headers in a single header request.

The Fix

Luckily, PHP is pretty cool in allowing us to override the headers that have already been set. Knowing we can do this, means we just needed to know all of the Access-Control-Allow-Headers values that we needed. In this case, it’s Authorization, Content-Type and X-WP-Wpml-Language.

We can adjust the code we used to allow the X-WP-Wpml-Language header initially to allow all of our headers, like this:

add_action( 'rest_api_init', function () {
    add_action( 'rest_pre_serve_request', function () {
        header( 'Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Wpml-Language', true );
    } );
}, 15 );

That second parameter of true in the header() function is the real hero here. It’s the replace parameter and allows us to replace any headers of the same name that have been set before.

Doing this allows us to send a single header with all of our allowed values in it so that IE11 can read them all.

Conclusion

This was probably the error that took me the longest to debug when building this system. The biggest issue for me was the lack of info around that IE11 won’t handle multiple headers of the same name, especially when it’s developer tools show them all in one line.

I’m hoping that by documenting this here, I’ll remember it and it may help somebody in the future with this issue.

About the author

Daryll Doyle

Daryll is a Staff Engineer at 10up.

With a deep passion for web development and open-source technology, Daryll is the original author of the SVG sanitization plugin Safe SVG, which boasts over 900,000 installs from the WordPress.org plugin directory and is now proudly maintained by 10up.

Throughout his career, Daryll has demonstrated expertise in SVGs and WordPress, sharing his knowledge through public speaking engagements, including a notable talk at WordCamp London 2018.

Beyond his professional life, Daryll enjoys spending quality time with his family and diving into research on Developer Experience, constantly seeking ways to improve the workflow and productivity of fellow developers.

Add comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Daryll Doyle WordPress Development and Consultancy in Cornwall