You try to upload a plugin ZIP, activate a theme, or drag a large image into the media library, and WordPress stops you with a white page that reads:
The link you followed has expired. Please try again.
No stack trace, no file path, no hint about what actually went wrong. The HTTP response code is 403. The message sounds like your login session timed out, but reloading and retrying does not help. The error comes from wp_nonce_ays(), which WordPress calls whenever a form submission fails its security nonce check. The misleading part is that a missing nonce and an expired nonce produce the same message, and the most common reason the nonce is missing has nothing to do with expiry.
What the error actually means (and why the message is misleading)
Every form in the WordPress admin area includes a hidden _wpnonce field generated by wp_nonce_field(). When you submit the form, check_admin_referer() reads that field and validates it. If the field is missing or invalid, WordPress calls wp_nonce_ays(), which kills the page with a 403 and the "link you followed has expired" text.
The word "expired" in the message implies time ran out. That is sometimes true, but in practice the most frequent trigger is that the nonce field was never received by WordPress at all, because PHP silently discarded the entire POST body before WordPress had a chance to read it. The error text has not been updated to reflect this. It dates back to WordPress 2.0.4 and has stayed the same ever since.
Common causes, most likely first
1. PHP upload limits too low (by far the most common)
When a file upload pushes the POST body past PHP's post_max_size, PHP does not return an error. It silently empties both the $_POST and $_FILES superglobals and hands the request to WordPress with nothing attached. The PHP manual on common upload pitfalls documents this behavior explicitly: "If the size of post data is greater than post_max_size, the $_POST and $_FILES superglobals are empty."
WordPress receives the empty $_POST, finds no _wpnonce field in it, and check_admin_referer() fails. The result is the "link you followed has expired" message, even though the real cause is a file that was too large and a PHP limit that was too small.
Two PHP directives control this:
| Directive | PHP default | What it controls |
|---|---|---|
upload_max_filesize |
2M |
Maximum size of a single uploaded file |
post_max_size |
8M |
Maximum size of the entire POST body (must be >= upload_max_filesize) |
Many shared hosts leave these at or near the PHP defaults. A plugin ZIP of 10 MB, a theme ZIP of 15 MB, or a batch of product photos can exceed post_max_size without you realizing the limit even exists.
2. Actual nonce expiry (page left open too long)
WordPress nonces are valid for 12 to 24 hours, depending on where in the tick cycle the nonce was generated. The default nonce_life is DAY_IN_SECONDS (86,400 seconds), divided into two 12-hour ticks. A nonce is valid for the current tick and the previous tick, giving it a window of 12 to 24 hours depending on timing.
If you open the plugin upload page on Monday morning and come back Tuesday to click "Install Now", the nonce in the form has expired. Reload the page. The new page contains a fresh nonce and the upload will work.
A subtler variant: a caching plugin serving stale admin-area HTML. Most caching plugins exclude /wp-admin/ by default, but misconfigurations happen. If WP Rocket, W3 Total Cache, LiteSpeed Cache, or WP Super Cache serves a cached copy of a page that contains a nonce, the nonce ages in cache. WP Rocket's documentation recommends a cache lifespan of 10 hours or less specifically to avoid serving stale nonces. If your cache lifespan is 24 hours or more, cached forms will contain expired nonces by the time they are served. For a broader look at cache bypass issues, see the troubleshooting article on why your WordPress cache is not working.
3. Plugin conflict
Security hardening plugins, firewall plugins, and WooCommerce extensions that hook into form processing can strip or modify $_POST data before WordPress reads the nonce. The symptom is identical: nonce missing, 403, "link expired". If the error started after installing or updating a specific plugin, that plugin is the prime suspect.
4. MIME type rejection on file upload
On some WordPress configurations, wp_check_filetype_and_ext() rejects an uploaded file's MIME type and the rejection surfaces as the "link expired" message rather than the more specific "this file type is not permitted" error. This is most common with non-standard file types like SVG, HEIC on older WordPress versions, or custom vendor formats. The dedicated article on file type upload restrictions covers the per-format fixes in detail.
How to diagnose which cause applies
Work through these checks in order. Stop at the first match.
Step 1: did the error happen during a file upload?
If you were uploading a plugin, theme, or media file when the error appeared, start with cause 1. Go to Tools > Site Health > Info > Server and check Upload max filesize and Post max size. Compare those values to the size of the file you were uploading. If the file is larger than either limit, you have found your cause.
The media library also shows the current cap at the bottom of the Media > Add New screen: "Maximum upload file size: X MB."
Step 2: was the page open for a long time before submitting? If you opened the admin page hours ago, or left a tab open overnight, the nonce may have genuinely expired. Reload the page and try again. If the error disappears, it was cause 2 and no configuration change is needed.
Step 3: is a caching plugin active?
Deactivate the caching plugin temporarily and retry the action. If the error disappears, the cache was serving stale nonces. Clear the full page cache, reduce the cache lifespan to 10 hours or less, and confirm that /wp-admin/ is excluded from page caching.
Step 4: did the error start after a plugin change? Deactivate the most recently installed or updated plugin and retry. If the error disappears, you have found the conflict. For a systematic approach when the culprit is not obvious, deactivate all plugins and reactivate them one at a time.
Step 5: were you uploading a non-standard file type? If the file was an SVG, HEIC, a custom font, or a vendor-specific format, cause 4 (MIME rejection) is likely. Check the file type upload restrictions article for the fix specific to your format.
Fix for upload size limits (cause 1)
Raise upload_max_filesize and post_max_size together. post_max_size must be equal to or larger than upload_max_filesize, always. If you only raise one, the lower value still wins.
The fastest route depends on your hosting setup:
- Host control panel (cPanel, Plesk, DirectAdmin, hPanel): find the PHP settings editor and raise both values to
64Mor higher. This is the cleanest method and works on the next request. .htaccess(Apache withmod_phponly): addphp_value upload_max_filesize 64Mandphp_value post_max_size 64M. Does not work on nginx or Apache with PHP-FPM..user.ini(PHP-FPM without panel access): create a.user.iniin the WordPress root withupload_max_filesize = 64Mandpost_max_size = 64M. Takes up to five minutes to apply due to PHP's.user.inicache TTL.
The full walkthrough with per-method verification, nginx-specific steps, the wp-config.php trap (why ini_set() does not work for these directives), and Multisite's separate cap is in the dedicated article on increasing the file upload size in WordPress.
You will know the fix worked when Tools > Site Health > Info > Server shows the new values for Upload max filesize and Post max size, and the upload that previously triggered the "link expired" error completes without it.
Fix for nonce expiry (cause 2)
If the cause is a genuinely expired nonce (page open too long, or cached forms), the fix depends on which variant applies.
Page left open too long: reload the page before submitting. That is the entire fix. The fresh page contains a fresh nonce valid for the next 12 to 24 hours.
Caching plugin serving stale nonces:
- Clear the full page cache from the caching plugin's dashboard.
- Set the cache lifespan to 10 hours or less. In WP Rocket, this is under Settings > WP Rocket > Cache > Cache Lifespan.
- Confirm that
/wp-admin/pages are excluded from page caching. Every major caching plugin does this by default, but check if someone disabled the exclusion.
Modifying nonce_life as a last resort. The nonce_life filter lets you change the nonce validity window. This is almost never the right fix. Increasing nonce lifetime widens the CSRF attack window on every form in WordPress. Only consider it when you have confirmed that actual nonce expiry is the cause, all caching is correctly configured, and no other option exists. Even then, do not exceed 48 hours.
// Last resort. Reduces security. Use only after confirming cause 2.
add_filter( 'nonce_life', function () {
return 48 * HOUR_IN_SECONDS;
} );
When to escalate
If you have checked the upload size limits, confirmed the nonce is not expired, cleared the cache, and deactivated plugins, and the error still appears, gather this before opening a support ticket:
- The exact action that triggers the error (which admin page, which button, whether a file upload was involved, the file's size).
- The
Upload max filesize,Post max size, andPHP SAPIvalues from Tools > Site Health > Info > Server. - Your WordPress version, PHP version, and active theme name.
- A full list of active plugins.
- Whether the site is single-site or multisite.
- Whether any caching plugin is active and what its cache lifespan is set to.
- The contents of the PHP error log around the time the error occurred (if you have access). The log may contain additional context that the white 403 page does not show.
How to prevent it from coming back
- Match your upload limits to the files you actually work with. If your workflow involves plugin ZIPs of 20 MB and media files of 50 MB, a
post_max_sizeof64Mprevents the most common trigger of this error. The default8Mis too low for most WordPress sites that do more than text editing. - Keep cache lifespan under 12 hours. This prevents cached nonces from expiring before the cached page is refreshed. Most caching plugins recommend 10 hours.
- Do not leave admin pages open overnight. If you tend to leave browser tabs open for days, reload the page before submitting any form. The nonce in the form ages even when you are not looking at it.