Key takeaways
- Check Media, then Library, before you retry. WordPress.com support notes that an upload can succeed even when the uploader reports an error, and retrying creates duplicates.
- In the browser’s Network panel, find the failed async-upload.php request. A 413 points at a size cap, a 500 at PHP failing, and a 403 at a security rule or an expired session.
- Tools, then Site Health, then Info, then Media handling shows the effective upload limit and whether GD or Imagick resizes your images.
- Raising PHP limits doesn’t lift a web-server or proxy cap. Kinsta warns that more memory per PHP thread can mean fewer threads.
- Hand it to the host when the evidence points at server configuration, a firewall rule or a missing image module. Give them the URL, the timestamp and the status code.
You drag a photo into Media, then Add New. The progress bar fills, then turns red with two words: “HTTP error.” There’s no status code, no file name and no hint of what failed. WordPress shows this one message for very different failures: the image was too big to resize, a firewall refused the request, PHP ran out of memory, the server couldn’t write the file, or your login session expired.
So the fix is never “raise the memory limit” by default. First find out which layer failed. That takes about five minutes, and the checks below need nothing more than your browser and wp-admin. If the whole site returns a 500 rather than only the uploader, follow our guide to fixing a server error 500 instead. That’s a different problem with a different starting point.
Did the upload actually fail?
Check first, because sometimes it didn’t. WordPress.com’s upload troubleshooting page notes that “Unexpected response from the server” can appear even when the file uploaded fine. The same goes for the plain HTTP error: the original file is saved, and then a later step such as making thumbnails or running an optimizer fails.
Open Media, then Library, and reload it. If the image is there, perhaps with a blank or grey thumbnail, the upload worked and the processing did not. That points at image resizing, not at upload limits. If you skip this check and retry five times, you end up with five copies.
How to find the real cause in five minutes
WooCommerce’s documentation on HTTP response codes recommends the same two sources for any HTTP error: the browser’s Network panel and the server logs. For uploads, add Site Health and a test image.
- Capture the failed request. Open your browser’s developer tools (F12 or Cmd+Option+I), go to the Network tab and upload the file again. Find the request to
async-upload.phpand note three things: the status code, the response body and the time. - Upload a small, known-good image. Use a screenshot of around 100 KB. If it uploads, the problem is something about the failing file, such as its size, dimensions or format. If it also fails, the problem is site-wide: permissions, a security rule, a plugin or the server.
- Check Site Health. Go to Tools, then Site Health, then Info. Under Media handling you’ll see the maximum upload file size, the max size of a POST request, the effective upload limit, and whether GD or Imagick is the active image editor. The Server section shows your PHP version.
- Read the log at that timestamp. Your host’s control panel usually has an error log. If it doesn’t, turn on WordPress logging in
wp-config.phpso errors go towp-content/debug.logand don’t show to visitors.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Remove these lines when you’re done. The log can contain file paths and other details you don’t want left on a live server.
Now match what you found against this table.
| What the Network panel shows | Where it points | First move |
|---|---|---|
| 413 Request Entity Too Large | A size cap on the web server, proxy or CDN, or PHP’s post_max_size | Compare the file size with Site Health; ask the host about the server limit |
| 500 | PHP failed, often running out of memory while resizing, or a plugin error | Read the log entry at the same time |
| 403, or a firewall block page in the response | A security plugin, ModSecurity or a host firewall rule, or an expired session | Log out and back in, then check the security plugin’s log or ask the host |
| 502, 504, or a long wait then failure | Processing took longer than the server allows | Try a smaller image; check any optimizer plugin |
| 200, but the response contains HTML or a PHP warning | A plugin or theme printed output that broke the response | Read the response body; run a conflict test |
Which cause fits what you found
These causes are listed in the order worth checking. The order is not measured. Nobody publishes data on how often each one happens, so use what you collected to decide which one applies.
The image is too big to process
This is the classic case. When a 12-megapixel phone photo uploads, WordPress makes several resized copies of it, and that work runs inside PHP’s memory and time limits. Newer WordPress versions sometimes show a clearer message for this, starting “The server cannot process the image,” which suggests uploading a smaller image. The log, if you have it, often says Allowed memory size of ... bytes exhausted.
The fastest fix is on your side. Resize the image to about 2560 pixels on the long edge before uploading. That’s the size where WordPress starts scaling large uploads down anyway, so a larger original rarely improves what visitors see. If you need full-size originals, ask the host about memory for image processing. Don’t just raise the limit blindly. Kinsta’s server-error docs point out that giving each PHP thread more memory can mean fewer threads, so a bigger limit can slow the rest of the site.
The image library itself can be the problem. In a resolved WordPress.org thread from 2024, images over roughly 1 MB timed out on a Bluehost site. Switching the default editor to GD with the “Default to GD” plugin fixed it, and uploads got faster. Treat that as a workaround to test, not proof that GD is better. Site Health tells you which editor is active before you change it.
A size limit at a layer WordPress cannot see
Upload size is capped in several places, and each cap works on its own. PHP has upload_max_filesize and post_max_size, and the lower one wins. The web server (for example nginx’s client_max_body_size), a reverse proxy and a CDN can each reject a request before PHP ever runs. Kinsta’s client-error docs say exactly this: oversized requests are rejected before WordPress receives them, and they can show up in the uploader as a general upload error.
This is why the popular php.ini and .htaccess snippets often do nothing. They raise a PHP limit while a server limit somewhere else stays where it was. A 413, or a failure that always starts at the same file size, means you should ask the host which layer has the cap. For very large files, Kinsta suggests skipping the browser: upload over SFTP, then import with WP-CLI using wp media import.
A plugin, theme or security rule is in the way
Image optimizers, offload-to-cloud plugins and security plugins all run during or right after an upload. If the error started after an update, or the response body contains a block page, one of them is the likely cause.
Don’t deactivate plugins on a live store to find out. The Health Check and Troubleshooting plugin has a Troubleshooting Mode that turns off plugins and switches to a default theme for your logged-in session only, so visitors and customers see the normal site. Test the upload there, then turn plugins back on one at a time until the error returns. Our guide to fixing plugins that are not working covers what to do with the culprit once you find it.
A 403 with a firewall page usually comes from the host’s ModSecurity or a similar rule. You can’t change that from wp-admin. Send the host the timestamp and ask which rule matched.
The server can’t write the file
Errors such as “could not be moved to wp-content/uploads/…” point at file writing: ownership, permissions or a full disk. Check disk space in your hosting panel first. Never fix this by setting the uploads folder to 777, because that opens the folder to anyone on the server.
Permissions are suspected more often than they’re guilty. In a 2025 WordPress.org thread, a small 600 by 800 image failed with exactly that “could not be moved” message. Replies suspected folder permissions, but the fix was the host updating the site’s PHP version. If the PHP version in Site Health is old, ask about that before you touch permissions.
The session or connection dropped
If the failure is one-off rather than repeatable, log out and back in, then try again in another browser or on another network. Make sure the address bar shows https:// in wp-admin when your site runs on HTTPS, because a mixed setup can break admin requests. If the multi-file uploader itself keeps failing, WordPress’s Media Add New screen offers a simpler browser uploader as a fallback.
Renaming a file to remove spaces or accented characters is cheap to try. It is rarely the actual cause of a repeatable HTTP error.
When to stop and get help
Stop troubleshooting on your own when any of these is true:
- You can’t get at the logs.
- The response is a host firewall page.
- Site Health shows a missing image module or a PHP version your host controls.
- You’ve worked through the checks above and the failing layer is still unclear.
From that point, more guessing costs hours, and many confirmed fixes in support threads came from the host.
Make the request specific. Send the host or developer:
- the page you uploaded from
- the exact time and time zone
- the status code and response body from the Network panel
- the file’s size and pixel dimensions
- what you’ve already ruled out
Then ask about the request-size limit, firewall or ModSecurity rules, PHP memory and timeouts for image processing, GD and Imagick, and disk space. A support request like that gets an answer. “Uploads are broken” gets a reply asking for more information.
If you’d rather hand the whole investigation over, SiteSelf can do the troubleshooting work on your site. With hosting (SSH) access, it reads the logs, makes the change and reports what it changed and what it checked.