You hit Save. WordPress answers with “Are you sure you want to do this?” and the work disappears.

That is a nonce error, a failed check on the one-time security token WordPress attaches to forms, admin URLs, and REST requests.

Frustrating, and almost never random. Expired tokens, cached pages, session mismatches, and low PHP limits cause nearly all of them.

What this guide covers:

  • How nonce verification works, tick by tick
  • What each error message actually means
  • Fixes for caching plugins, CDNs, AJAX calls, and the WooCommerce Store API
  • CSP and Ethereum nonce errors, which share the name and nothing else

By the end, you will diagnose the cause in minutes instead of deactivating plugins at random.

What Is a Nonce Error

A nonce error is a failed validation of a one-time security token that WordPress attaches to forms, admin URLs, and API requests. The server rejects the submission because the token is missing, expired, or was generated for a different user, action, or login session.

Nonce stands for “number used once”. WordPress breaks that definition twice over: its nonces are hashed strings of letters and numbers, and the same value keeps working until it expires.

What a nonce error blocks:

  • Plugin and theme installs from the dashboard
  • Media library uploads
  • Settings saves inside wp-admin
  • WooCommerce add-to-cart and checkout submissions
  • Any AJAX handler wired to checkajaxreferer()

The failure surfaces as a 403 Forbidden response, which is why people file it under permissions. It isn’t one.

Key difference: a permissions error means the user lacks the capability. A nonce error means the request could not be proven to originate from your own site.

Have you seen the latest WordPress statistics?

Discover the latest WordPress statistics: market share, security trends, performance data, and revenue insights that shape the web.

Check Them Out →

Patchstack logged 7,966 new vulnerabilities across the WordPress ecosystem in 2024, up 34% on 2023, and CSRF appears in that count exactly because developers skip the token check.

How Does Nonce Verification Work

Nonce verification compares the token sent with a request against a hash the server rebuilds on the spot. WordPress hashes 4 inputs together: the action string, the user ID, the session token, and the current nonce tick. Matching hash, request proceeds.

Where the token travels: a hidden form field named wpnonce, a URL query parameter, or the X-WP-Nonce header on REST calls. It sits inside the page HTML in plain sight.

wpverifynonce() does not return a simple true or false. It returns the tick number, and that number tells you how old the token is.

Return valueMeaningAge of token
2Matches the current tickUnder 12 hours
1Matches the previous tick12–24 hours
falseNo tick matchesNonce error

The session token is the part that catches people out. Because wpgetsessiontoken() feeds the hash, a nonce minted during one login dies the second that session ends.

WordPress core itself has been caught out here. CVE-2017-9063 covered a CSRF hole in the filesystem credentials dialog before 4.7.5, where no nonce was required at all.

What Causes a Nonce Error

5 causes account for nearly every nonce error: an expired tick, a session or cookie mismatch, a cached page serving a stale token, a mismatched action string, and PHP limits that print the same message. The first three fill most support queues.

Expired Nonce Tick

Default lifetime is 86,400 seconds, split into two ticks of 43,200 seconds each.

Real validity runs between 12 hours plus one second and a full 24 hours, depending on where in the tick the token was created (WordPress developer documentation).

Classic trigger: a draft tab left open overnight, then submitted with coffee in hand the next morning.

Session and Cookie Mismatch

Logout in another tab: kills the session token, invalidates every nonce already rendered.

Cookie expiry: the browser drops wordpressloggedin, so the user ID feeding the hash drops to 0.

Regenerated salts: new security keys in wp-config.php void every nonce and auth cookie site-wide, instantly.

Mismatched WordPress Address and Site Address settings (one with www, one without) produce the same symptom and often get filed as a login problem instead.

Cached Pages Serving Stale Tokens

SQ Magazine’s 2025 market data puts caching plugin adoption at 82.4% of WordPress sites, which explains why this cause dominates on live sites.

Full-page caching stores the token along with the markup. Thirteen hours later the HTML is still fresh enough to serve and the nonce inside it is already dead.

Wrong Action String

A nonce created for one action never validates against another. The strings have to match character for character.

wpcreatenonce('savesettings') checked with checkadminreferer('save-settings') fails silently. One hyphen. That’s the whole bug.

Shield Security research notes a single character error in validation logic can break CSRF protection outright.

Server Limits Misread as Nonce Errors

PHP kills oversized POST requests before WordPress ever runs the token check, and the browser lands on the same “expired” screen.

  • postmaxsize lower than the uploaded ZIP
  • uploadmaxfilesize below the theme file size
  • maxinputvars capped on forms with hundreds of fields

Related territory: a 413 request entity too large response and failures where images refuse to upload trace back to the same three settings.

What Do the Common Nonce Error Messages Mean

WordPress prints 5 different messages for what is often the same underlying failure. The wording rarely describes the actual cause, so map the message to its real trigger before touching any settings.

MessageUsual real causeWhere it appears
The link you followed has expiredPHP upload or POST size limitTheme and plugin uploads
Are you sure you want to do this?check_admin_referer() failurewp-admin forms and action URLs
Cookies are blocked due to unexpected outputSession token unavailable when the page rendersLogin screen
rest_cookie_invalid_nonce (403)Stale nonce in cached HTMLBrowser console and REST API calls
Invalid nonceCached cart fragments or stale checkout dataWooCommerce cart and checkout

“The link you followed has expired” is the biggest liar of the group. It reads like a timing problem and is almost always a server limit, which is why it shows up when plugin installs refuse to run.

Where to confirm the cause: the WordPress error log for PHP notices, the server error log for limit rejections, and the browser Network tab for the raw 403 response body.

Anything failing mid-update belongs in the same bucket as other plugin update errors, since both stall on a failed token check before any files move.

How to Fix a Nonce Error in WordPress

Work from fastest to most invasive. A page refresh regenerates the token against the current tick and clears roughly half of all reported cases, so start there before editing a single file.

  1. Refresh the page and resubmit the action
  2. Hard refresh, then clear cookies for the site domain
  3. Log out, log back in, retry
  4. Purge cache in order: plugin cache, object cache, CDN, browser
  5. Raise PHP limits if the error only fires on uploads
  6. Deactivate plugins in batches to isolate a conflict
  7. Switch to a default theme to rule out theme-level form handling

Raising PHP Limits

Set postmaxsize higher than uploadmaxfilesize, never equal. Setting them to the same value still fails, and plenty of tutorials get that wrong.

Common working pair: uploadmaxfilesize = 64M and postmaxsize = 128M, applied through php.ini, .htaccess, or your host’s PHP selector.

Low limits also produce the memory exhausted error, so raise memorylimit in the same pass.

Isolating a Plugin or Theme Conflict

Batch deactivation beats one-at-a-time on sites carrying 40 plugins. Halve the list, test, halve again.

Tools worth having open: Query Monitor for hook-level inspection, the Health Check and Troubleshooting plugin for a safe per-user test mode, and WP-CLI for wp cache flush.

If the error survives every plugin being off, the theme is next. A broken form handler in a child theme sits in the same family as other theme errors, and switching to Twenty Twenty-Four confirms it in under a minute.

Adjusting Nonce Lifetime

The noncelife filter takes a value in seconds and applies globally, including wp-admin, AJAX, and REST authentication.

Drop it into a snippet plugin or functions.php: addfilter('noncelife', fn() => 4 HOURINSECONDS);

Extending the lifetime to paper over a caching problem is a bad trade. Longer tokens mean a wider CSRF window, and the cache is still the thing that’s broken.

How Do Caching Plugins and CDNs Break Nonce Validation

Full-page caching and per-user tokens conflict by design. The cache stores one copy of the HTML for everyone, nonces are bound to a single user and session, so every visitor after the first receives a token that was never theirs.

WordPress core contributor Milana Cap puts the rule plainly: keep the cache lifespan shorter than the nonce lifespan.

LayerWhat to excludeVerification
Page cache pluginLogged-in users, wp-admin, cart, checkoutResponse shows a cache MISS
CDN edge?wc-ajax=, /wp-json/, /wp-admin/, /my-account/, cart, checkout, authenticated requestscf-cache-status: **BYPASS**
Object cacheNothing by default, but flush after Redis restartswp cache flush returns success

Configuration notes by tool:

  • WP Rocket: cache lifespan defaults have caused invalid nonces at 24 hours; the team’s own issue thread settled on a 10 hour lifespan
  • LiteSpeed Cache: ESI needs LiteSpeed Enterprise, OpenLiteSpeed will not hold nonces correctly
  • W3 Total Cache: exclude admin URLs before enabling any logged-in caching
  • Cloudflare APO and Varnish: bypass on wordpressloggedin and woocommercecarthash cookies

Real case: Meow Lightbox 5.5.1 fired restcookieinvalidnonce for every visitor on LiteSpeed-cached pages after 12 to 24 hours, because the token was printed inline and cached with the page.

WooCommerce carries extra exposure since the 8.3 release in November 2023 made Cart and Checkout blocks the default. Cart fragments and ?wc-ajax= endpoints belong on the never-cache list, permanently.

How to Fix Nonce Errors in AJAX and REST API Requests

Custom code fails for 3 reasons: the token never reaches the request, the action strings disagree between creation and verification, or the token is stale before the user clicks anything. Each has a different fix.

Passing the Token to AJAX Calls

Hand the nonce to JavaScript with wplocalizescript() or wpaddinlinescript(), then post it back under ajaxnonce.

Matching pair: wpcreatenonce('myaction') on the PHP side, checkajaxreferer('myaction') in the handler. Same string, both ends.

An Ajax request missing the token returns -1 with a 403, which lands in the console as a failed resource load rather than anything mentioning nonces.

REST API Requests and X-WP-Nonce

Cookie-authenticated REST calls need the X-WP-Nonce header carrying a nonce created for the wprest action. No header, no authentication, regardless of a valid login cookie.

In the block editor, wp.apiFetch handles this through createNonceMiddleware, which appends the header to every request without repeating it per call.

The package also retries: when a response comes back as restcookieinvalidnonce, api-fetch pulls a fresh token from the nonce endpoint and repeats the request. Custom clients built against the REST API get no such safety net.

The Logged-Out Edge Case

Guests all share one nonce, because the user ID feeding the hash is 0 for everyone.

A token generated while logged out then verified while logged in fails, which is exactly what happens when a visitor logs in at checkout on a cached page.

WooCommerce’s own tracker flagged this in the Store API: the fix is fetching the nonce from response headers at runtime rather than serializing it into the page at render time.

How Long Does a Nonce Stay Valid and How Is the Lifetime Changed

A WordPress nonce carries a default lifetime of 86,400 seconds. Real validity lands somewhere between 12 hours plus one second and a full 24 hours, depending on where in the tick the token was minted.

The noncelife filter changes that number globally, and it hits everything: wp-admin, AJAX handlers, REST authentication, plugin forms.

ValueSecondsWhat it controls
Nonce life (default)86,400Maximum nonce lifetime
Single tick43,200Half the nonce lifetime; one 12-hour validation window
Auth cookie default172,800Authentication session length without “Remember Me”

The Grace Period Between Ticks

Two ticks, not one. WordPress validates tokens from the current tick and the previous one, which is what creates the variable window.

A nonce created at 11:59 in the morning dies at midday. One created at 12:01 survives until midday the following day.

WordPress developer documentation confirms wpverifynonce() returns the tick number rather than a boolean, so the return value doubles as an age check.

Changing Lifetime With the noncelife Filter

The filter takes seconds and returns seconds. Nothing else.

addfilter('noncelife', function() { return 4 HOURINSECONDS; });

WordPress 6.1 added an $action argument to wpnoncetick(), so lifetimes can now target one action instead of the whole install.

Shorter values tighten the CSRF window. They also mean users on slow forms hit failures more often, so 4 hours is roughly the floor for anything public-facing.

Why Extending Lifetime Backfires

Stretching noncelife to 72 hours to stop cache-related failures treats the symptom and widens the attack window at the same time.

The WP Rocket team ran into this directly. Their own issue thread on invalid nonces at a 24-hour cache lifespan settled on dropping the cache to 10 hours rather than inflating the token lifetime.

Donation Platform for WooCommerce documents the same rule of thumb for its checkout forms: cache around 4 hours, leave the nonce alone.

How Do Login Sessions and Salt Keys Trigger Nonce Failures

Every nonce is bound to a session token and a user ID. Anything that changes either one, a logout, an expired cookie, a rotated key, or a role change, invalidates every token already sitting in a rendered page.

Session Token Binding

wpgetsessiontoken() feeds the hash directly, so tokens are per-session rather than per-user.

“Log out of all other sessions” in the profile screen kills every nonce generated under those sessions, instantly and site-wide.

Two-factor setups that force a logout every 48 hours produce the same effect mid-edit, which is exactly the complaint logged in WordPress Trac ticket 29312.

Regenerating Security Keys and Salts

WordPress stores 8 secret values in wp-config.php: AUTHKEY, SECUREAUTHKEY, LOGGEDINKEY, NONCEKEY, and their four matching salts.

NONCEKEY and NONCESALT sign the tokens. Replace them and every existing nonce dies with every auth cookie.

Useful after a breach, and worth knowing before you go editing config files for unrelated reasons. The Salt Shaker plugin handles rotation without FTP.

Multisite and Cookie Scope

  • COOKIEDOMAIN set to a single subdomain on a multi-domain network
  • SITECOOKIEPATH pointing at the wrong install path
  • Mismatched www and non-www between Site Address and WordPress Address

Each one breaks the login cookie before the token check runs, which is a familiar headache for anyone running several WordPress sites off one network.

Reverse Proxies and Role Changes

Proxies: nginx or Varnish stripping cookies before PHP sees them leaves the user ID at 0 during hash generation.

User switching plugins: the session token changes on every switch, so any page left open under the previous identity fails.

Role changes: capability changes do not invalidate nonces on their own, though the follow-up capability check will still reject the request.

What Is a Content Security Policy Nonce Error

A CSP nonce error is a browser-side block, not a server rejection. The browser refuses to run an inline script or style because its nonce attribute does not match the value in the Content-Security-Policy response header.

Same word, completely different mechanism from the WordPress token. Nothing about PHP is involved here.

The Console Message

Chrome prints: refused to execute inline script because it violates the following Content Security Policy directive.

The message then lists the fix options, per the OWASP cheat sheet: the unsafe-inline keyword, a sha256 hash, or a matching nonce.

Inline CSS throws a parallel message about refusing to apply inline style, usually falling back to default-src when style-src was never set.

Why a CSP Nonce Cannot Be Cached

One nonce per response. The header and the markup ship together, so a cached page pairs an old header value with old script tags and the browser blocks everything.

Reusing a fixed nonce across responses (the pattern in plenty of Apache config snippets floating around) removes the protection entirely. An attacker just reads the value and reuses it.

OWASP recommends a random value generated per request, which rules out full-page caching of the HTML carrying it.

strict-dynamic and Fallbacks

strict-dynamic tells the browser to trust scripts created by an already-trusted script, so nonces do not have to be propagated by hand.

You get one or the other. Browsers treat unsafe-inline as ignored the moment a nonce or hash appears in the same directive, and combining both is not the workaround people expect.

W3C’s own spec discussion flags nonce exfiltration through content attributes, and Dropbox published a widely referenced deployment writeup on this exact tension.

Reporting Violations

Run report-uri or report-to before enforcing anything.

Report-only mode logs every violation without breaking the page, which catches third-party tags nobody remembered installing.

On WordPress specifically, blocked inline scripts overlap with warnings that scripts are loading from unauthenticated sources, since both trace back to what the page is allowed to execute.

What Is an Ethereum Transaction Nonce Error

An Ethereum nonce is a sequential counter of transactions sent from an address, starting at 0. A nonce error means the number attached to your transaction does not match what the network expects next, so the transaction is rejected or queued indefinitely.

MessageCauseFix
Nonce too lowThe nonce has already been used on-chainSync with the account’s current pending/latest nonce and use the next available nonce
Nonce too highThere is a gap because an earlier nonce is still missingSubmit the missing transaction first, or use the correct pending nonce
Replacement transaction underpricedA transaction with the same nonce already exists, but the replacement fee is not high enoughResubmit with a sufficiently higher fee; the exact required bump depends on the network/client

Stuck Pending Transactions

One stalled transaction blocks every later one from the same address. Nonce 13 and nonce 14 cannot confirm while nonce 12 sits in the mempool.

Fix the oldest nonce first. Everything else is wasted effort until that one clears.

Two routes: speed up (same nonce, higher fee) if you still want the transaction, or cancel with a zero-value transfer to your own address using that same nonce.

The 10% Gas Rule

Geth only accepts a replacement if the gas price beats the pending one by at least 10%. It is spam protection, not a protocol rule, and different clients apply it differently.

Thirdweb’s error documentation gives the working example: a first transaction at 30 gwei maxFeePerGas needs roughly 40 gwei on the retry.

Resetting Local Nonce Data

MetaMask renamed this in extension version 10.28.1, from “reset account” to “clear activity and nonce data”. Mobile still calls it reset account.

It wipes local transaction history and nonce tracking only. On-chain assets stay untouched, and MetaMask advises using it only when the transaction is not visible on a block explorer.

Check the real count first with ethgetTransactionCount or the nonce field on Etherscan before touching anything. Hardhat and ethers.js both manage nonces automatically unless you override them, and overriding them is where most scripted failures start.

How Are Nonce Errors Prevented

Prevention is configuration, not troubleshooting. Cache exclusions written once at the server level, PHP limits set above realistic file sizes, and token refresh logic on long forms remove nearly every recurring failure before a user ever sees it.

LayerStanding ruleCheck
CacheNever cache admin, cart, checkout, or account pagesCache lifespan is shorter than the relevant token/nonce lifetime
PHPSet upload and POST limits above the largest plugin/theme ZIPSite Health → Info → Server
Custom codeUse unique action strings for each security-sensitive handlerPerform a capability check alongside every nonce/token check

Refreshing Tokens on Long Forms

WordPress core already solves this for the post editor. The Heartbeat API calls wprefreshpostnonces() and wprefreshheartbeatnonces(), which returns a fresh restnonce and heartbeat token on each tick.

Borrow the pattern: hook wprefreshnonces for custom screens, or expose a small endpoint your frontend can call before submitting.

Multi-step checkouts and long application forms are the obvious candidates. Anything a user might leave open through a lunch break qualifies.

Code Review Checklist for Custom Plugins

  • One unique action string per handler, matched exactly at both ends
  • checkadminreferer() for admin forms, checkajaxreferer() for AJAX
  • currentusercan() on every request, not the token check alone
  • No token verification skipped on GET actions that change data

Shield Security research calls out the second-to-last item as the frequent miss: plenty of vulnerable plugins validate the token and then never check what the user is allowed to do.

Monitoring Instead of Waiting for Tickets

The wpverifynoncefailed hook fires on every failed check, which makes silent logging trivial.

Log the action string, user ID, and referring URL. A cluster of failures on one endpoint points straight at a cache rule or a mismatched string.

WP Statistics shipped a CSRF hole through version 13.1.1 from missing token validation on a single function, the kind of gap that shows up in logs long before it shows up in a vulnerability database. Worth reading alongside broader questions about why a WordPress site gets flagged as not secure.

FAQ on Nonce Error

What does a nonce error actually mean?

The server rejected your request because its security token failed verification. WordPress could not confirm the submission came from your own site, so it stopped the action before anything was saved, uploaded, or installed.

How do I fix “The link you followed has expired”?

Raise your PHP limits. That message usually points at uploadmaxfilesize and postmaxsize being smaller than the theme or plugin ZIP, not at an expired token. Set postmaxsize higher than uploadmaxfilesize.

Why does my nonce expire so quickly?

It probably did not. A cached page serving old markup, a logout in another tab, or regenerated salt keys all invalidate tokens well before the 24-hour lifetime runs out.

Does clearing the cache fix a nonce error?

Often, yes. Purge in order: caching plugin, object cache, CDN, then browser. If the error returns hours later, the real fix is a cache exclusion rule rather than another manual purge.

Can a plugin cause a nonce error?

Yes, and it is common. Custom code that creates a token for one action string and verifies it against another fails every time. Deactivate in batches to find the culprit.

How long is a WordPress nonce valid?

Between 12 hours plus one second and a full 24 hours. WordPress splits the 86,400 second lifetime into two ticks and accepts tokens from the current tick and the previous one.

Why does WooCommerce show “invalid nonce” at checkout?

Cached cart fragments, almost always. Exclude cart, checkout, my-account, and ?wc-ajax= URLs from every cache layer, including your CDN, then clear everything and retest in incognito.

What does restcookieinvalidnonce mean?

Your REST request carried a stale or missing X-WP-Nonce header. The response is a 403. Cookie-authenticated calls need a fresh token created for the wprest action on every page load.

Is a nonce error a sign my site was hacked?

Rarely. Nonces block CSRF attempts, so a failure can mean the system worked, but expired tokens and cache conflicts explain the overwhelming majority of reports.

How do I fix “nonce too low” in MetaMask?

Clear activity and nonce data in Settings, Advanced. Check the real count on Etherscan first. If a transaction is stuck pending, resend it with the same nonce and at least 10% more gas.

Conclusion

A nonce error is a diagnosis problem, not a mystery. The message on screen rarely names the real trigger, so work from the failing layer down.

Check the age of the page first, then the login session, then every cache sitting in front of it.

Three habits kill most repeat failures:

  • Cache exclusion rules covering admin, cart, and ?wc-ajax= paths
  • Matching action strings on both sides of every verification call
  • A capability check sitting beside the token check, always

Keep Query Monitor open and the wpverifynoncefailed` hook logging on staging.

Log the action string alongside the user ID. Patterns surface there long before anyone opens a support ticke