Skip to content

WordPress maintenance

“The link you followed has expired” in WordPress: the upload limit behind it

In short

WordPress shows "The link you followed has expired" when an upload is bigger than PHP's post_max_size. PHP drops the whole request, so the security check fails. On our 13 Oct 2026 test site a 9 MB plugin zip hit it at the 8 MB limit. Raising post_max_size and upload_max_filesize in php.ini fixed it.

Key points

  • A 9 MB plugin zip against PHP's stock 8 MB post_max_size gave "The link you followed has expired." with HTTP 403.
  • A 3 MB zip against the stock 2 MB upload_max_filesize gave a different message that names php.ini.
  • Raising the limits in php.ini or in .htaccess fixed it on our Apache server. The same lines in wp-config.php and .user.ini did nothing there.
  • Raising only upload_max_filesize was not enough. WordPress shows the smaller of the two limits as the maximum upload size.
On this page (10)
  1. What does “The link you followed has expired” mean in WordPress?
  2. How did we test it?
  3. What did each upload size show?
  4. Which fixes raised the maximum upload size?
  5. Why did wp-config.php and .user.ini not work?
  6. Why is raising upload_max_filesize alone not enough?
  7. How do you fix “The link you followed has expired”?
  8. What did we not test?
  9. Is an upload failing on your site right now?
  10. Questions and answers

It usually means your upload was too big for PHP. The message comes from WordPress’s security check on admin forms, which is only the messenger.

When a request is larger than PHP’s post_max_size, PHP empties the form data before WordPress reads it. The PHP manual says so directly: the $_POST and $_FILES superglobals are empty. The security token goes with them, the check fails, and WordPress prints the expired-link page from wp_nonce_ays().

Upload faults like this are one of the fixes on our WordPress care and fixes page, where we work on a copy of the site first.

How did we test it?

On 13 Oct 2026 we ran WordPress 6.9.4 on PHP 8.2 and Apache in Docker, with a MariaDB 11 database. The server had PHP’s stock limits: upload_max_filesize 2M and post_max_size 8M.

A script logged in as an admin and uploaded three plugin zips through Plugins > Add New > Upload: a 214-byte plugin, a 3 MB one and a 9 MB one. It sent the two large zips through the plain form on Media > Add New too, and recorded the status and page text each time.

What did each upload size show?

UploadWhat WordPress showedStatus
214-byte plugin zipPlugin installed successfully.200
3 MB zip, plugin uploadThe uploaded file exceeds the upload_max_filesize directive in php.ini.500
9 MB zip, plugin uploadThe link you followed has expired.403
3 MB zip, media upload formThe uploaded file exceeds the upload_max_filesize directive in php.ini.500
9 MB zip, media upload formThe form came back empty, with no message at all200

The 9 MB page gives no hint about size. The Apache error log does: “PHP Warning: POST Content-Length of 9438127 bytes exceeds the limit of 8388608 bytes”. If your host gives you error logs, that line names the cause.

Which fixes raised the maximum upload size?

We set each fix to 16M for uploads and 20M for posts, then ran the same uploads again.

What we changedMaximum upload file size shown3 MB zip9 MB zip
Nothing (PHP stock limits)2 MBFailedLink expired
Two php_value lines in .htaccess16 MBInstalledInstalled
Two ini_set lines in wp-config.php2 MBFailedLink expired
A .user.ini file in the site root2 MBFailedLink expired
php.ini, upload_max_filesize raised, post_max_size left at 8M8 MBInstalledLink expired
php.ini, both raised, web server restarted16 MBInstalledInstalled

Why did wp-config.php and .user.ini not work?

PHP will not let ini_set change these two settings at all. The PHP manual marks upload_max_filesize and post_max_size as PHP_INI_PERDIR, which means php.ini, .htaccess, httpd.conf or .user.ini only. The upload is also read before WordPress loads, so wp-config.php would be too late even if PHP allowed it. On our server the limit stayed at 2 MB with the lines in place.

.user.ini is a different story. The PHP manual on .user.ini files says PHP reads them only under the CGI/FastCGI SAPI. Our server ran PHP as an Apache module, so it ignored the file. On a host that runs PHP-FPM it may well work. We did not test that.

The .htaccess lines work the other way round. They need PHP running as an Apache module, which is what we had. We did not try them on any other setup, so test them on a copy of the site first.

Why is raising upload_max_filesize alone not enough?

Because the expired-link error comes from post_max_size. We raised upload_max_filesize to 16M and left post_max_size at 8M. The 3 MB zip installed; the 9 MB zip still said “The link you followed has expired.”

WordPress showed 8 MB as the maximum upload size in that state. wp_max_upload_size() takes the smaller of the two values, so the number on Media > Add New is the one to trust.

  1. Check the size of the file you are uploading.
  2. Open Media > Add New and read “Maximum upload file size”.
  3. If the file is bigger, raise post_max_size and upload_max_filesize together, with post_max_size the larger of the two. If your host has a PHP settings screen, change them there; otherwise they go in php.ini.
  4. Restart PHP or ask the host to, then read the Media > Add New line again before you retry the upload.

Step 4 saved us from a false result. The first time we raised the limits, the page still said 2 MB: our new settings file had not reached the server at all. We only trusted a fix once the number on that page moved.

If you cannot change PHP settings, you can also upload the plugin or theme folder over SFTP into wp-content and activate it from the admin. That skips the upload form entirely.

If the file is small and WordPress still refuses it, the cause is the file’s type, not its size: see why WordPress says you are not allowed to upload this file type.

This is a fix we do as a one-off job. We find which limit your host enforces, raise it in the place your server actually reads, and test an upload before we hand it back.

PHP limits cause other failures too. Our post on the WordPress memory exhausted error covers memory_limit, the next setting people hit after the upload limit.

What did we not test?

  • Hosts that run PHP-FPM, LiteSpeed or nginx, where .user.ini and the host’s control panel take the place of .htaccess.
  • The drag-and-drop media uploader in a real browser. We used the plain upload form.
  • Theme uploads and the block editor’s upload button.
  • Uploads through the REST API.

Is an upload failing on your site right now?

Tell us the file size and the message you see, and we will tell you which limit is in the way.

Where did these facts come from?

Questions and answers

Is "The link you followed has expired" a security problem?

Not in the upload case. The form's security token was sent, but PHP threw away the request because it was too big, so WordPress never saw the token.

Where do I see my site's maximum upload size?

Go to Media > Add New. The line "Maximum upload file size" shows the smaller of PHP's upload_max_filesize and post_max_size.

Can I fix it in wp-config.php?

No. PHP does not let ini_set change upload_max_filesize or post_max_size at all; the PHP manual lists both as settable only in php.ini, .htaccess, httpd.conf or .user.ini. On our test server two ini_set lines in wp-config.php left the limit at 2 MB.

Still stuck? Send us the page address and what you see