WordPress REST API error
rest_cookie_invalid_nonce.
This error means WordPress reached cookie/session authentication, but the X-WP-Nonce value did not validate for the current logged-in session.
Failure stage
Endpoint reached: the request got to WordPress REST handling.
Cookie auth attempted: WordPress treated the browser session as the auth path.
Failed first: the nonce is stale, missing, generated for the wrong action, or from another session.
Not reached yet: route permission callback and business logic may not have run.
Nonce evidence matrix
| Evidence | Likely cause | First check |
|---|---|---|
{"code":"rest_cookie_invalid_nonce"} | Cookie nonce rejected | Regenerate nonce with wp_create_nonce('wp_rest'). |
X-WP-Nonce present but still 403 | Nonce from another user/session | Generate it in the same logged-in browser that sends the request. |
| Works after refresh, fails later | Cached or expired nonce | Do not cache localized nonce values for long-lived pages. |
| External script sends nonce | Wrong auth model | Use Application Passwords instead of browser cookie nonces. |
| Request is cross-origin | Cookie not sent or blocked | Check same-origin path, credentials mode, and cookie policy. |
Do this first
Do this
- Confirm the request is same-origin and sent by a logged-in user.
- Regenerate the nonce immediately before the page script uses it.
- Send it as
X-WP-Nonce, not inside a JSON body. - Use the REST debugger with the exact response JSON.
Do not do this yet
- Do not loosen
permission_callbackto bypass the nonce error. - Do not switch to admin cookies for external scripts.
- Do not treat this as a route-not-found problem.
- Do not disable security plugins until nonce/session evidence is checked.
Bad request pattern
fetch('/wp-json/my-plugin/v1/save', {
method: 'POST',
headers: { 'X-WP-Nonce': cachedNonceFromYesterday },
body: JSON.stringify({ title: 'Draft' })
});
Corrected pattern
// Pattern to verify in your environment.
wp_localize_script('my-app', 'IFK_REST', [
'root' => esc_url_raw(rest_url()),
'nonce' => wp_create_nonce('wp_rest'),
]);
fetch(IFK_REST.root + 'my-plugin/v1/save', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': IFK_REST.nonce
},
body: JSON.stringify({ title: 'Draft' })
});