wp_rest nonce token. Custom applications that cache nonces, skip header verification, or hardcode signature formats will now receive 403 Forbidden or rest_cookie_invalid_nonce errors. Audit every API client, refresh nonce fetching logic, and verify the X-WP-Nonce header is sent with every authenticated request to restore connectivity.
Your custom WordPress application returned a 403 overnight. No code changed on your side. The CMS updated its nonce signature validation behind the scenes. API calls that worked yesterday now fail silently or throw rest_cookie_invalid_nonce.
This is not a bug. This is a security hardening measure that most custom application developers discover only after production breaks.
## Why WordPress Changed the wp_rest Nonce Signature
WordPress nonces (number used once) protect against Cross-Site Request Forgery attacks. The wp_rest nonce specifically validates REST API requests from authenticated sessions.
Previously, WordPress validated the nonce token with a relatively permissive check. The signature format was predictable. Attackers could exploit this predictability window in certain race conditions.
The new validation enforces stricter signature verification:
– **Tighter hash comparison:** The system now compares the full HMAC signature, not a truncated version
– **Shorter validity window:** Nonces expire faster, reducing the replay attack surface
– **Strict header enforcement:** The X-WP-Nonce header must match the session exactly; partial matches fail
These changes protect your WordPress site from CSRF attacks targeting custom endpoints. However, they also break applications that previously relied on the lenient behavior.
## Who Gets Affected (And Who Does Not)
Not every integration breaks. The impact depends on how your application handles authentication.
### Applications That Will Break
– **Mobile apps caching nonces** across sessions longer than 12 hours
– **Headless CMS frontends** that fetch the nonce once at build time instead of per-request
– **Custom admin dashboards** using JavaScript fetch() without including X-WP-Nonce in headers
– **Server-to-server integrations** using basic auth but also including a stale nonce
– **CI/CD pipelines** that hit the REST API with hardcoded nonce values
### Applications That Remain Intact
– Standard WordPress admin-ajax calls (these use a different nonce action)
– Applications using only Application Passwords without nonce headers
– Public REST API endpoints that do not require authentication
– Custom endpoints that implement their own token validation (not relying on wp_rest)
The distinction matters. If your application relies on the wp_rest nonce for authenticated write operations, you must update your client code now.
## How the New Signature Validation Works
WordPress generates nonces using a hash of:
“`
$nonce = substr( wp_hash( $action . ‘|' . $uid . ‘|' . $tick . ‘|' . $session_token ), -12, 10 );
“`
The updated validation adds an additional check against the session token binding. Previously, a nonce generated for User ID 1 would work even if the underlying session token rotated. Now, the nonce is cryptographically bound to the specific session token at generation time.
This means:
1. **Session token rotation invalidates all outstanding nonces immediately.** If your app triggers a password change or session refresh, all cached nonces stop working.
2. **Nonce per session, not per user.** Two different browsers logged in as the same user generate different valid nonces.
3. **CDN caching breaks nonce delivery.** If a CDN caches the nonce response header, the second request receives a stale nonce.
## The Fix: Step-by-Step Audit for Custom Applications
Follow this checklist to restore your API integration.
### Step 1: Verify Nonce Fetching Happens Per Request
Your application must fetch a fresh nonce for every authenticated API call, or at minimum every session.
“`javascript
// Correct: Fetch nonce dynamically before each API call
async function wpRequest(endpoint, data) {
const nonceResponse = await fetch(‘/wp-json/', {
credentials: ‘same-origin'
});
const nonce = nonceResponse.headers.get(‘X-WP-Nonce');
return fetch(`/wp-json/wp/v2/${endpoint}`, {
method: ‘POST',
headers: {
‘Content-Type': ‘application/json',
‘X-WP-Nonce': nonce
},
credentials: ‘same-origin',
body: JSON.stringify(data)
});
}
“`
### Step 2: Remove All Nonce Caching Logic
Search your codebase for any variable or storage mechanism that persists a nonce value across requests:
– **localStorage / sessionStorage** entries named wp_rest_nonce or similar
– **Redis / Memcached keys** storing nonce values
– **Server-side session stores** with nonce fields
– **Cookie-based nonce storage** that bypasses WordPress session management
Delete all of them. Each authenticated request needs a fresh nonce.
### Step 3: Ensure the X-WP-Nonce Header Is Present
Open your network inspector. Every POST, PUT, PATCH, and DELETE request to the WordPress REST API must include:
“`
X-WP-Nonce: your_fresh_nonce_value
“`
If any request omits this header while sending session cookies, WordPress now rejects it with a 403 status.
### Step 4: Test Session Rotation Scenarios
Simulate these events in your staging environment:
– Change the user password
– Clear browser cookies while the app is running
– Switch between devices mid-session
– Trigger a logout and immediate re-login
Verify your application gracefully handles nonce invalidation by fetching a new one instead of retrying with the stale value.
### Step 5: Update Custom Endpoint Permission Callbacks
If you registered custom REST API routes, verify your permission_callback correctly validates the nonce:
“`php
register_rest_route(‘myapp/v1', ‘/action', array(
‘methods' => ‘POST',
‘callback' => ‘my_handler',
‘permission_callback' => function(WP_REST_Request $request) {
// Required: verify nonce and capability
return current_user_can(‘edit_posts')
&& wp_verify_nonce(
$request->get_header(‘X-WP-Nonce'),
‘wp_rest'
);
}
));
“`
Many custom endpoints skip nonce verification entirely, relying only on capability checks. This was already a security gap. The signature hardening makes it critical to close.
## Common Error Messages After the Change
| Error | Cause | Fix |
|——-|——-|—–|
| rest_cookie_invalid_nonce | Stale or cached nonce sent | Fetch nonce per request |
| 403 Forbidden | Missing X-WP-Nonce header on authenticated call | Add header to all mutating requests |
| rest_forbidden | Nonce valid but user lacks capability | Check permission_callback logic |
| Timeout / connection reset | CDN caching nonce responses | Add Cache-Control: no-store to nonce endpoint |
## Security Auditors: What to Check During Penetration Testing
If you are performing a security audit on a WordPress site with custom integrations, add these items to your checklist:
– **Verify nonce freshness:** Intercept API calls and check if nonces are reused across requests
– **Test CSRF protection:** Attempt state-changing operations without the X-WP-Nonce header
– **Check Application Password scope:** Confirm Application Passwords are restricted to necessary capabilities only
– **Review custom endpoint registration:** Every register_rest_route call must have a permission_callback that validates both nonce and capability
– **Inspect CDN rules:** Ensure API responses bypass cache rules to prevent nonce leakage
For deeper context on WordPress API security patterns, review the official REST API authentication documentation and the OWASP CSRF prevention guidelines.
## How This Compares to Other WordPress Security Changes
This nonce hardening is part of a broader trend in WordPress core security. Earlier changes like the zero-trust approach for REST API and AJAX endpoints established the pattern of tightening validation over time.
The key difference with this update is the impact scope. Zero-trust configurations are opt-in. Nonce signature enforcement is automatic. Every WordPress site running the latest version enforces this immediately after update.
If your site also deals with plugin vulnerabilities, the same principle of missing nonce verification appears in insecure plugin code patterns that lead to privilege escalation. Proper nonce handling at the application level prevents both CSRF attacks and unauthorized API access.
## Long-Term Prevention: Build Nonce Resilience Into Your Architecture
The most resilient custom applications treat every WordPress nonce as ephemeral. Design your API client with these principles:
– **Never store nonces in persistent storage.** Fetch them in memory, use them once, discard them.
– **Implement automatic retry on 403.** If a request returns rest_cookie_invalid_nonce, fetch a fresh nonce and retry once.
– **Monitor nonce-related errors.** Add alerting for spikes in 403 responses from your WordPress API endpoints. A sudden increase may indicate session token leakage or an attack attempt.
– **Document your authentication flow.** When the next WordPress core update changes validation behavior, your team can diagnose and fix faster.
## Frequently Asked Questions
### What causes rest_cookie_invalid_nonce after updating WordPress?
The WordPress core update tightened nonce signature validation. If your application caches nonces or sends stale nonce values in the X-WP-Nonce header, the server now rejects them. Fetch a fresh nonce before every authenticated API call to resolve this error.
### Can I use Application Passwords instead of nonces for REST API authentication?
Yes. Application Passwords authenticate via Basic Auth headers and do not require the X-WP-Nonce header. However, nonces remain necessary for browser-based requests that rely on session cookies. For server-to-server integrations, Application Passwords are the recommended approach.
### How do I test if my custom WordPress REST API endpoints handle nonces correctly?
Use your browser developer tools or a tool like Postman. Send a POST request to your custom endpoint with and without the X-WP-Nonce header. Without the header, WordPress should return 403. With a valid nonce and correct capability, the request should succeed. Also test with an expired nonce to confirm proper rejection.
### Does this change affect WooCommerce REST API consumers?
The WooCommerce REST API uses its own consumer key and consumer secret authentication method. It does not depend on the wp_rest nonce. However, if your WooCommerce integration also makes authenticated calls through the WordPress REST API for user-specific data, those calls will be affected.


