Thursday, June 25, 2026
All guides
Troubleshooting guide Media

WordPress HTTP Error When Uploading Images: The Real Fix

The vague 'HTTP error' on the media uploader has four real causes. Here is how I diagnose and fix it on client sites in under 10 minutes.

Arjun Mehta Published June 23, 2026 Last reviewed June 23, 2026 10 min read Step-by-step walkthrough
Reviewed and tested by the WPRescue team on a real WordPress install before publishing. How we test fixes
WordPress media uploader showing HTTP error message

What's Happening

You drag a JPG into the WordPress media library and a red bar appears saying 'HTTP error'. No error code, no explanation, no clue what went wrong. The cause is almost always one of four things: PHP memory, file size limits, the image processor library, or a security rule on the server.

The WordPress 'HTTP error' on the media uploader is the most misleading error message in the entire admin. It tells you nothing useful, it has no error code, and the WordPress documentation page on it lists eight unrelated causes. After a decade of debugging it on client sites, I can tell you the cause is almost always one of four things, and you can usually fix it in under 10 minutes if you know where to look.

This guide walks through the exact order I work in, what each fix actually does, and how to confirm the cause so the error does not silently come back the next time someone uploads a 6 MB photo from an iPhone. I have also included the snippets I drop into functions.php on sites that hit the issue weekly, so the uploader becomes self-healing.

Step 1: Rule Out the Browser and the File

Before touching the server, eliminate the easy stuff. Try the upload again in a different browser. Try in incognito with all extensions disabled. Try a different file, ideally a small PNG. If a 50 KB PNG uploads fine but a 4 MB JPG fails, the problem is server-side memory or file size limits, not WordPress.

Also rename the file. WordPress sanitizes filenames but some plugins and CDNs do not, and a filename with spaces, accented characters, or symbols like # and & is enough to trip a WAF rule. A plain lowercase name like photo.jpg is the safest test.

Step 2: Enable Debug Logging and Reproduce the Error

Open wp-config.php and add the three debug constants below. They make WordPress write the real PHP error to /wp-content/debug.log without showing it to visitors. Reproduce the upload, then open the log file. The most recent line is almost always the answer.

phpAdd these four lines above the 'That's all, stop editing!' line in wp-config.php.
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', 0);

Step 3: Raise PHP Memory and Upload Limits

On 70 percent of the HTTP error tickets I see, raising memory and upload limits fixes it instantly. Add the lines below to wp-config.php and a .user.ini file in your site root. If your host blocks .user.ini, ask support to raise the limits at the PHP level.

iniReasonable defaults for a typical WordPress site that handles photo uploads.
; .user.ini in your site root
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300

Step 4: Force the GD Image Library

WordPress picks Imagick over GD by default when both are installed. Imagick is more powerful but it also crashes more often on shared hosts because it forks subprocesses for each resize. Forcing GD is harmless on standard sites and resolves a large share of HTTP errors on hosts running old Imagick builds.

phpDrop this into your child theme's functions.php or a small mu-plugin.
add_filter('wp_image_editors', function($editors) {
    return ['WP_Image_Editor_GD', 'WP_Image_Editor_Imagick'];
});

Step 5: Check Cloudflare, Sucuri, and Other WAFs

If you sit behind Cloudflare, log in, go to Security > WAF, and look at recent blocked requests filtered by your own IP. A WAF rule that blocks multipart POST is a very common cause of the HTTP error after the site is otherwise healthy. The fix is either to pause Cloudflare for the test or add a bypass rule for /wp-admin/.

Sucuri and other cloud firewalls have similar logs in their dashboard. The Ray ID or request ID in the firewall log is the fastest way to confirm it was the WAF and not WordPress that rejected the upload.

WordPress media library with image optimization plugin settings open
Image optimization plugins can also trigger HTTP errors during upload. Test with them disabled.

Step 6: Disable Image Optimization Plugins One at a Time

Imagify, Smush, ShortPixel, and EWWW Image Optimizer all hook into the upload process to compress or resize new images. If any of them is misconfigured or has a stale API key, the entire upload fails with the same generic HTTP error.

Deactivate each one in turn and retry the upload. When the upload succeeds, you have your culprit. Reactivate it and check its settings for an expired API key, a quota cap, or an unsupported format.

When the Error Is Intermittent

An HTTP error that happens once a day, only on big files, with no pattern, is almost always memory. Shared hosts give you a memory ceiling and your site hovers near it during traffic spikes. When an upload comes in at the wrong moment, PHP runs out of RAM mid-resize and the worker dies.

If you cannot raise memory further on your host, the long-term fix is to move off the shared plan or to resize images before uploading. ShortPixel Adaptive Images and similar tools offload the heavy lifting to a third-party service so your server never has to resize a 6 MB photo.

Why Imagick Crashes More Often Than People Admit

Imagick is a PHP wrapper around the ImageMagick command-line tool. Every time WordPress resizes an image with Imagick, PHP shells out to a separate ImageMagick process, that process opens the file, builds the thumbnail, and writes the output. On a healthy server this is invisible. On a busy shared host with capped processes per user, it is one of the most common sources of silent upload failures.

The trouble is that ImageMagick respects its own policy.xml file, which can be tuned to deny memory, disk, or even specific file formats. Many shared hosts ship an aggressive policy.xml that denies PDFs, denies certain MIME types, or caps memory at 64 MiB. When you upload a 5 MB JPG, Imagick tries to open it, hits the cap, and exits with status 1. WordPress sees the failed worker and shows you 'HTTP error'.

If you have shell access, run `convert -list policy` to see your host's current limits. If you do not, the safest move is to force GD as shown earlier in this guide. GD lives inside the PHP process, has no separate policy file, and is more predictable on shared hosting even though it is technically slower for large batch jobs.

Filename Sanitization Edge Cases That Still Bite in 2026

WordPress sanitizes filenames before saving them, but the sanitization is conservative. Emojis in a filename used to crash uploads outright, that is fixed, but filenames with Arabic, Hebrew, or right-to-left characters still confuse some hosts because the filesystem stores the bytes one way and the database stores them another. When the file path round-trips through both layers, the lookup can fail and WordPress reports the upload as broken even though the file landed on disk.

Two-byte characters in iPhone screenshot filenames (the curly apostrophe and the en dash that Apple silently inserts) are another quiet cause. A filename like 'IMG_2026'06'25.HEIC' contains characters that look like normal apostrophes but are U+2019. WordPress accepts them, the database row writes fine, but some CDNs reject the URL when the file is later requested, which makes the upload look like it failed even though the asset is there.

When in doubt, rename the file before testing. A plain ASCII filename like test-upload.jpg eliminates a whole category of false positives in under three seconds.

How to Tell the Difference Between an HTTP Error and a Timeout

WordPress shows the same 'HTTP error' message for two completely different failures. The first is an immediate failure where the AJAX request returned a non-200 status. The second is a timeout where the request never got an answer at all because the worker died mid-resize. The fix differs.

Open the browser DevTools network tab, retry the upload, and watch the async-upload.php request. If it returns within a second with status 500, you have a PHP fatal that landed in your debug.log. If it spins for 30 seconds and then shows 'failed' with no status code, you hit a server timeout and the fix is on the memory or worker side, not in the code.

This single check saves about 20 minutes per ticket. Without it you end up raising memory limits to fix a PHP fatal or chasing a fatal that does not exist because the real cause was a 30-second proxy timeout in front of PHP.

Permanent Hardening: A Small mu-Plugin That Catches Future Failures

After cleaning up the immediate failure, drop the snippet below into /wp-content/mu-plugins/upload-debug.php. It logs every failed upload with the PHP error message, the filename, the user ID, and the memory peak. The next time someone reports 'I tried to upload a photo and it broke', you have a real log entry instead of a vague complaint.

phpDrop-in mu-plugin that records every failed upload attempt with enough context to debug it later.
<?php
// /wp-content/mu-plugins/upload-debug.php
add_filter('wp_handle_upload_prefilter', function ($file) {
    if (!empty($file['error'])) {
        error_log(sprintf(
            '[upload-debug] user=%d file=%s size=%d error=%s peak_mem=%s',
            get_current_user_id(),
            $file['name'] ?? 'unknown',
            $file['size'] ?? 0,
            $file['error'],
            size_format(memory_get_peak_usage(true))
        ));
    }
    return $file;
});

Checklist Before You Hand the Site Back to the Client

Before closing the ticket, run through a five-point check so the same issue does not surface again next week. Upload a small PNG, a 5 MB JPG, a HEIC straight from an iPhone, a WebP, and a PNG with a transparent background. If all five succeed, the uploader is properly fixed across the formats real users send.

Then revisit the Media Library and make sure the thumbnails generated cleanly. A successful upload that produced no thumbnail is still a half-broken upload and tells you the image library is starved of memory even though the original landed on disk. Regenerate Thumbnails plugin can backfill any half-finished assets in one click.

  • Test JPG, PNG, WebP, HEIC, and a transparent PNG in sequence
  • Confirm thumbnails generated for every test file
  • Tail /wp-content/debug.log during each test for hidden warnings
  • Verify Cloudflare WAF event log shows no blocked requests
  • Document the final memory_limit and upload_max_filesize values in a site README so the next admin does not undo them

Complete Fix Checklist

  1. 1Try the upload again after waiting 60 seconds, the error is sometimes a transient server timeout.
  2. 2Rename the file to lowercase with no spaces or special characters and retry.
  3. 3Raise PHP memory_limit to 256M and upload_max_filesize to 64M in php.ini or .user.ini.
  4. 4Force WordPress to use the GD image library by adding a small filter to functions.php.
  5. 5Disable image optimization plugins (Imagify, Smush, ShortPixel) one at a time and retry the upload.
  6. 6If you use Cloudflare, temporarily pause it and retry, an over-zealous WAF rule blocks multipart uploads on some sites.

Quick Tips

  • Try a different browser before assuming server-side
  • Test with a small PNG to rule out file size
  • Check /wp-content/debug.log immediately after a failed upload for the real PHP error

Frequently Asked Questions

Why does WordPress just say 'HTTP error' with no details?
The media uploader uses an AJAX call to wp-admin/async-upload.php. When that script fails, WordPress only gets back a generic failure status, not the underlying PHP error. The real cause is in your /wp-content/debug.log or your host's PHP error log, which is why I always enable WP_DEBUG_LOG before retrying.
Is the HTTP error caused by my image being too large?
Sometimes. PHP's upload_max_filesize and post_max_size both apply. If your file is larger than either of those, the upload fails before it even reaches WordPress. Check Media > Add New, the page shows your real limit at the bottom.
I see the error only on big JPGs, not small ones. Why?
Big JPGs need lots of RAM to resize. A 6 MB photo can need 200 to 300 MB of memory while WordPress is generating thumbnails. Raise WP_MEMORY_LIMIT to 256M and the error usually disappears.
Does the HTTP error mean my host blocked the upload?
It can. ModSecurity rules on shared hosts sometimes block multipart uploads that look suspicious. If you have shell access, check /var/log/modsec_audit.log. If not, open a ticket with your host and ask them to whitelist your IP for /wp-admin/async-upload.php.
Will switching from Imagick to GD break my images?
No. GD is the original PHP image library and handles JPG, PNG, GIF, and WebP fine on PHP 8.0 and newer. The only thing you lose is some advanced format support like multi-page TIFF, which WordPress does not use anyway.
Does the error mean my SSL is broken?
Rarely. A mixed-content site can cause an HTTP error if the upload URL is HTTPS but the site URL is HTTP. Make sure WP_HOME and WP_SITEURL in wp-config.php both match your real URL with https://.
Can a CDN cause the HTTP error?
Yes, especially Cloudflare and BunnyCDN. Their WAF can block large or unusual POST requests. Either pause the CDN to confirm, or add a Page Rule that bypasses the WAF for /wp-admin/.
Why does the error appear only for one user?
Browser extensions, especially ad blockers and security suites, can interfere with the uploader. Test in incognito with all extensions disabled before blaming the server.

Related Guides