Goal: I want to cache the full WordPress response via NGINX and exclusively use the REST API for the user specific parts.

Issue: I can't get an authenticated nonce to use against the API.

My plan was to expose a rest endpoint that returns a valid nonce shown below:

add_action( 'rest_api_init', function () {
  register_rest_route( 'myplugin/v1', '/nonce', array(
    'methods' => 'GET',
    'callback' => function() {
      $logged_in = is_user_logged_in();

      $response = [
        'logged_in' => $logged_in,
        'nonce' => wp_create_nonce( 'wp_rest' )
      return new WP_REST_Response( $response, 200 );
  ) );

  register_rest_route( 'myplugin/v1', '/auth', array(
    'methods' => 'GET',
    'callback' => function() {
      return new WP_REST_Response( [ 'user_id' => get_current_user_id() ], 200 );
  ) );
} );

I'd then do an call to the REST endpoint with no nonce to get the unauthenticated nonce. Then use the unauthenticated nonce to get the authenticated version like so:

jQuery(window).load(function() {

    jQuery.get('/wp-json/myplugin/v1/nonce').done(function(res) {
        console.log( 'Got unauthenticated nonce: ' + res.nonce );

            url: '/wp-json/myplugin/v1/nonce',
            method: 'GET',
            beforeSend: function( xhr ) {
                    xhr.setRequestHeader( 'X-WP-Nonce', res.nonce );
        }).done( function( res ) {
            console.log( 'Got authenticated nonce: ' + res.nonce );
        } );;



Then I'd store the authenticated nonce in local storage and use that in conjunction with the X-WP-Nonce response header to keep it fresh. For some reason that doesn't work despite the authentication cookies being passed as part of the request. What am I missing?

Update: I've found at least two methods to deal with this issue.

1) Skip the nonce token altogether by using JWT (for example via the plugin JWT Authentication for WP REST API). No more CSRF but hang on cause XSS comes into the picture.

2) Via NGINX SSI (Server Side Includes). Basically the only part not cached is the creation of the nonce value. A downside to this approach is if you wanted to use Edge Side Includes (ESI) with your CDN you couldn't.

Note: If I authenticate (send username/password) via a custom REST endpoint I can get a valid authenticated nonce at that point but I'm back to square one when that nonce expires (the default is 24 hours).

  • Are you sure that you're not caching the nonce endpoint too? Keep in mind too that nonces expire, and for authenticated requests you also need a cookie – Tom J Nowell Apr 10 '18 at 21:37
  • Yes, I'm sure. My local environment isn't caching the request at all. My plan is to refresh the nonce token using the X-WP-Nonce HTTP header but I can't get past never getting an "authenticated" nonce. – JSP Apr 10 '18 at 21:44
  • so is it the initial nonce that isn't being accepted? You've outlined the flow of execution but you never said which step it failed at – Tom J Nowell Apr 10 '18 at 21:52
  • It fails at the second nonce attempt (the "authenticated" one where I'm passing the nonce as a HTTP header X-WP-Nonce) with {"code":"rest_cookie_invalid_nonce","message":"Cookie nonce is invalid","data":{"status":403}} – JSP Apr 10 '18 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.