So you’ve created a custom form on WordPress that you’ve protected with a
wp_nonce_field(). On the backend, you then call
wp_verify_nonce() to validate the request to your custom WordPress REST API endpoint. Perfect! When you go to test the form submission, however, you sometimes get an
HTTP 400 response. That’s odd. Why does this happen?
How WordPress Generates Nonce Values
If you review the source code of
wp_create_nonce(), you’ll see that WordPress hashes various values together to create the nonce. In particular, the current user’s ID from
wp_get_current_user() and the current user’s session token from
wp_get_session_token() are part of the hash.
This means that nonces are tied to the current user’s identity.
How WordPress Authenticates REST API Requests
You may be surprised to find that WordPress does not recognize the
logged_in cookie by default when processing REST API requests. The Authentication section of WordPress’s REST API Handbook states:
For developers making manual Ajax requests, the nonce will need to be passed with each request. The API uses nonces with the action set to wp_rest. These can then be passed to the API via the _wpnonce data parameter (either POST data or in the query for GET requests), or via the X-WP-Nonce header. If no nonce is provided the API will set the current user to 0, turning the request into an unauthenticated request, even if you’re logged into WordPress. […] It is important to keep in mind that this authentication method relies on WordPress cookies. As a result this method is only applicable when the REST API is used inside of WordPress and the current user is logged in.
This means the WordPress REST API will not recognize the logged in user unless the request includes the
wp_rest nonce value as the
_wpnonce data parameter or the
X-WP-Nonce header value.
When no user is logged into WordPress,
wp_get_current_user() returns a
WP_User with ID 0 (zero) and no capabilities. This expresses an anonymous, public user, which is what the WordPress REST API uses by default.
wp_verify_nonce() Fails in Custom WordPress REST API Endpoints
Okay, so how does all of this tie together? Well, when you display
wp_nonce_field() in your theme template file, shortcode output, or otherwise hook into WordPress’s display of the current page, the logged-in WordPress user is automatically recognized. This means that the nonce field is calculated with that user’s ID and
logged_in cookie token.
When that nonce value is then passed to the WordPress REST API backend,
wp_verify_nonce() is then validating the value for the default, anonymous user with ID 0 (zero) and an empty
logged_in cookie token.
This means the ingredients for the nonce are different, thus validation fails and the WordPress REST API returns an
HTTP 400 response code.
If you’re going to perform identity-dependent actions within WordPress REST API endpoints, remember to properly authenticate the requests! This may be as simple as including the following nonce field in your form, depending on your processing:
wp_nonce_field( 'wp_rest', '_wpnonce', true, true );
Remember, though, that this nonce only helps determine the current user’s identity within WordPress REST API endpoints. I recommend that you still additionally output and verify your own custom nonce to ensure the user wanted to perform the specified action.