Question

My wordpress site sits behind Akamai, which is a cacheing service similar to Cloudflare.

I make the following API call:

GET /wp-json/mytheme/v1/get-posts?post_type=videos

This is done using apiFetch from '@wordpress/api-fetch';

And it automatically includes this in the request header

X-WP-Nonce: 12323423

This works fine until 24 hours later, when the nonce expires. The cache still continues to use the expired Nonce resulting in a 403 forbidden and a broken page.

If I make the same request without Nonce header, it works perfectly fine.

Is there a way in Wordpress to disable or remove the Nonce for GET requests only?

Or even strip out the X-WP-Nonce header by intercepting the Request?

This my code for making the request which is being made from the wordpress frontend.

apiFetch({
     path: '/wp-json/mytheme/v1/get-posts?post_type=videos',
     parse: false,
});
Was it helpful?

Solution

Based on the authentication documentation here - a nonce key needs to be passed with each request.

So if the nonce key is being cached on the frontend beyond its lifespan, you will need to hook into the API request before the authentication step and replace the cached nonce key with a valid one.

WordPress provides a rest_send_nocache_headers filter for us to hook into (See here). This lets us perform an action before the authentication.

$send_no_cache_headers = apply_filters('rest_send_nocache_headers', is_user_logged_in());
if (!$send_no_cache_headers && !is_admin() && $_SERVER['REQUEST_METHOD'] == 'GET') {
    $nonce = wp_create_nonce('wp_rest');
    $_SERVER['HTTP_X_WP_NONCE'] = $nonce;
}

In the above example, we hook into the filter passing the is_user_logged_in() function as the parameter. This will return true or false.

Then in our query, if the user is not logged in, they are not in the admin and, this is a GET request we proceed with switching the invalid nonce key with a valid one.

OTHER TIPS

Just to add to the accepted answer, I found a similar solution but instead hooked into rest_authentication_errors prior to rest_cookie_check_errors running.

Since the underlying issue is nonce expiration, it's possible you could have this problem when the user is logged in (i.e. when no cache headers are sent) as well as when logged out. I also put some checks in to ensure we're dealing with just our REST request - I checked the 'rest_route' query var but there may be a better way of doing this.

add_filter( 'rest_authentication_errors', function( $errors ) {
        // Bail if rest_route isn't defined (shouldn't happen!)
        if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
            return $errors;
        }

        $route = ltrim( $GLOBALS['wp']->query_vars['rest_route'], '/' );

        // Ensure we're dealing with our REST requst.
        if ( 0 !== strpos( $route, 'my-awesome-namespace/v1' ) ) {
            return $errors;
        }

        if ( ! empty( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
            $nonce = $_SERVER['HTTP_X_WP_NONCE'];

            if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
                // Nonce check failed, so create a new one.
                $_SERVER['HTTP_X_WP_NONCE'] = wp_create_nonce( 'wp_rest' );
            }
        }

        return $errors;
    }, 10 );
Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top