I would like to understand the best practices regarding nonce validation in REST APIs.

I see a lot of people talking about wp_rest nonce for REST requests. But upon looking on WordPress core code, I saw that wp_rest is just a nonce to validate a logged in user status, if it's not present, it just runs the request as guest.

That said, should I submit two nonces upon sending a POST request to a REST API? One for authentication wp_rest and another for the action foo_action?

If so, how should I send wp_rest and foo_action nonce in JavaScript, and, in PHP, what's the correct place to validate those nonces? (I mean validate_callback for a arg? permission_callback?)


I'm answering my own question based on my tests. I asked this because it's something that I could not find googling, so I wanted to post a solution here.

First thing I did was to enqueue the JavaScript using wp_localize_script function, to pass data from PHP to JavaScript:

wp_register_script('foo', 'foo.js');

wp_localize_script('foo', 'fooData', [
    'endpoint' => esc_url_raw(rest_url('/foo/v1')),
    'nonces' => [
        'foo_nonce' => wp_create_nonce('foo_nonce'),
        'wp_rest'   => wp_create_nonce('wp_rest')


We are passing 2 nonces, foo_nonce and wp_rest. WordPress checks wp_rest nonce internally for every REST API request made from a logged-in user. (That check is in wp-includes\rest-api.php @ 810 - WordPress 5.0.2). If that nonce is not present, the request is treated as if it were a guest user.

Now, in the JavaScript, we receive the data from PHP in the FooData object:

    url: FooData.endpoint + '/addSomething',
    type: "POST",
    dataType: "json",
    data: {
        // Your other data here
        "foo_nonce": FooData.nonces.foo_nonce
    // This nonce is verified automatically by WordPress for logged-in users
    beforeSend: function (xhr) {
        xhr.setRequestHeader('X-WP-Nonce', FooData.nonces.wp_rest);

When the request is sent to the server, we can run wp_verify_nonce on the validate_callback of the nonce field:

register_rest_route('foo/v1', '/doSomething', [
    'methods' => WP_REST_Server::CREATABLE,
    'callback' => [$this, 'doSomething'],
    'args' => [
        // Your other data here
        'foo_nonce' => [
            'validate_callback' => function($foo_nonce) {
                return wp_verify_nonce($foo_nonce, 'foo_nonce');

That's it!

Your Answer

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

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