I am generating a nonce to be used with client side fetch requests to the rest api:

function prepAllAdminScripts() {
    , PLUGIN_FOLDER_URL . 'shared/adminArea.bundle.js'
    , null
    , null
    , true

  wp_localize_script('reactAdminArea', 'serverParams', [
    '_wpnonce' => wp_create_nonce('wp_rest')
    , 'apiBaseUrlFromWp' => get_rest_url()

add_action('admin_enqueue_scripts', 'prepAllAdminScripts');

As the fetch request is fired, I see it go into an auth handler function. The nonce value shows as a string and appears to be a matching nonce value but it fails:

  protected function authUserOrRespondWithError($clientNonce) { // 3a730<partially removed>
    $verification = wp_verify_nonce($clientNonce); // false

    if ($verification === false) {
      wp_send_json(['error' => "Failed nonce verification"]); // sends back to the client

When I check the nonce value in the Chrome browser console, it matches exactly.

Does anyone know why the verification is returning false here?


Here is how I am sending the nonce data back to the API:

const fetchAllCoupons = async () => {
    try {
      const response = await fetch(`${apiBaseUrl}/pluginFolder/1.0/loadAll`, {
        method : 'post',
        headers : {
          'Content-Type' : 'application/json'
        body : JSON.stringify({clientNonce})
      let data = await response.json();

      if (data && 'error' in data) {

      return data;
    catch (e) {
      console.log(e, `=====error=====`);

Here is the route handler:

  public function registerLoadCouponRoute() : void {
    register_rest_route($this->urlBase, 'loadAll', [
      'methods' => ['get', 'post'],
      'callback' => [$this, 'respondAllCoupons']

Here is the first part of the callback that contains authUserOrRespondWithError:

  public function respondAllCoupons(\WP_REST_Request $request) {
    global $wpdb;

    $clientNonce = $request->get_param('clientNonce');
  • 1
    How are you sending the nonce? From the URL as a query param and how you capture it in your REST API? Sep 6, 2019 at 0:00
  • @RoelMagdaleno I send it in the post body of a fetch request. I have updated the original post to show the request handler. I capture it with a rest route handler using the callback shown. As mentioned, I can see the value arrives and is correct
    – Sean D
    Sep 6, 2019 at 6:59

1 Answer 1


First, nonce for an action is the same when the user is not authenticated/logged-in. (because the user ID is always 0)

Secondly, you're getting false because you didn't specify the nonce action which is wp_rest in your case:

// Correct usage - a valid action name is set (wp_rest).
wp_verify_nonce( $clientNonce, 'wp_rest' );

// Incorrect usage - empty action; always returns false.
wp_verify_nonce( $clientNonce );

And if you are actually trying to authenticate the request using the standard cookie authentication, then you should use the X-WP-Nonce header to send the nonce and not the request body/payload:

const fetchAllCoupons = async () => {
  try {
    const response = await fetch(`${apiBaseUrl}/pluginFolder/1.0/loadAll`, {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': clientNonce // <- here, send the nonce via the header
      body: JSON.stringify({
        // WordPress will not use this when verifying the associated user in the request.

        // but WordPress will not use this, either. Because we're sending a JSON payload.
        _wpnonce: clientNonce
  } catch (e) {

Or you can also send the nonce as a query string (in the URL) like so:

`${apiBaseUrl}/pluginFolder/1.0/loadAll?_wpnonce=` + clientNonce
  • Thank you, I have updated the post with a new issue while trying to follow your cookie authentication
    – Sean D
    Sep 7, 2019 at 18:04
  • Looking at the Update 2, temporarily add echo wp_create_nonce('wp_rest'); to the prepAllAdminScripts(). Are you seeing the same long-length nonce??
    – Sally CJ
    Sep 8, 2019 at 0:40
  • 1
    my mistake, I was trying to encrypt the nonce before but now that it has no place or reason to be decrypted, I have removed it. It works now
    – Sean D
    Sep 8, 2019 at 9:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.