You drag an image into the Media Library and the progress bar fills, then turns red. Depending on your WordPress version, you see “HTTP error.”, “Post-processing of the image failed likely because the server is busy or does not have enough resources”, or the current wording: “The server cannot process the image. This can happen if the server is busy or does not have enough resources to complete the task. Uploading a smaller image may help. Suggested maximum size is 2560 pixels.” Other files fail with “Sorry, you are not allowed to upload this file type.” or “Unexpected response from the server. The file may have been uploaded successfully.”
These messages come from different stages of the upload, and each stage has its own causes: PHP memory and the image library for processing errors, an allow-list for file type errors, and folder permissions or a firewall for the rest. This guide is for site owners, designers and editors who cannot get media into WordPress. If the error says the file “exceeds the maximum upload size for this site”, that is a size limit, covered in the upload limits guide.
What is happening
An upload from the Media Library goes to /wp-admin/async-upload.php. WordPress checks the file type, moves the file into wp-content/uploads/YYYY/MM/, and then, for images, opens the file with an image editor library to create the thumbnail, medium, large and theme sizes. Only after all of that does it send a JSON reply to the browser.
That last step is where large photos fail. Decoding a 24 megapixel photo needs a lot of memory and CPU, and it happens while the browser waits. If PHP runs out of memory or time, the reply never arrives, and the browser shows the generic processing message even though the original file is already saved.
| Message |
Stage |
Usual cause |
| HTTP error / The server cannot process the image / Post-processing failed |
Creating image sizes |
Memory or time limit, Imagick resource limits, huge pixel dimensions |
| Unexpected response from the server |
Any stage |
PHP fatal error, firewall block, or a plugin printing output into the JSON reply |
| Sorry, you are not allowed to upload this file type. |
Type check |
Extension not on the allow-list, or content does not match the extension |
| Unable to create directory wp-content/uploads/YYYY/MM. Is its parent directory writable by the server? |
Saving |
Permissions, ownership, wrong upload path, full disk |
| The uploaded file could not be moved to wp-content/uploads/YYYY/MM. |
Saving |
Permissions or full disk |
| Missing a temporary folder. / Failed to write file to disk. |
PHP receiving the file |
PHP temporary directory missing, unwritable or full |
| The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading. |
Browser, before upload |
WebP or AVIF file, but the server image library cannot edit that format (HEIC shows a similar message) |
Before you start
You need an administrator login, and SSH or SFTP plus your hosting panel for the server steps. Back up wp-config.php before editing it.
How to run commands: SSH into the server and change into the site folder, the one containing wp-config.php. WP-CLI may be called wp or wp-cli. When you create or change files, do it as the site user, not as root, so ownership stays correct. Browser checks use the developer tools (F12). The diagnostic tools help with checks from a browser.
Step 1: Read the error log for the upload
Every other step gets faster once you know what PHP said. Upload the failing file again, note the time, and open the PHP error log from your hosting panel (or wp-content/debug.log if WP_DEBUG_LOG is on). Look for lines at that minute.
PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 94371840 bytes) in /PATH/wp-includes/class-wp-image-editor-gd.php on line LINE_NUMBER
This is example log output, not a command. 268435456 bytes is 256 MB, the memory WordPress asked for while processing the image. The file name tells you which editor was running: class-wp-image-editor-gd.php or class-wp-image-editor-imagick.php. Other lines to look for: “Maximum execution time of SECONDS seconds exceeded” (a time limit), messages mentioning ImagickException or “cache resources exhausted” (Imagick limits, Step 3), and “Permission denied” or “failed to open stream” (Step 5).
If there is no PHP line, open the Network tab, upload again and click the async-upload.php request. A 403 with an HTML page points to a firewall (Step 6). A 500 or 502 with no PHP log line can mean the PHP worker was killed for memory or time; ask your host to check the PHP-FPM log.
Reproduce on the command line
wp media import IMAGE_PATH --porcelain
IMAGE_PATH is the path to the image on the server, uploaded by SFTP. --porcelain prints only the new attachment ID on success. Good output is a number. Bad output is an error message, which is often clearer than the browser one. WP-CLI uses the command-line PHP settings, which may have a higher memory limit than the web server, so a CLI success with a browser failure itself points to web server limits.
Step 2: Image processing errors, memory and the image editor
Confirm which editor runs and its limits
Go to Tools, Site Health, Info, Media Handling. It shows the active editor (WP_Image_Editor_Imagick or WP_Image_Editor_GD), the ImageMagick and GD versions, the Imagick resource limits, and the formats each supports. WordPress tries Imagick first and falls back to GD.
Why big photos run out of memory
An image is decoded into raw pixels before it is resized. At roughly 4 bytes per pixel, a 6000 by 4000 photo is 24 million pixels, about 96 MB, before any copies are made for resizing. The JPEG file might be only 8 MB. File size tells you little; pixel dimensions tell you the cost.
During image work WordPress raises the PHP memory limit to WP_MAX_MEMORY_LIMIT, which defaults to 256 MB, through the image_memory_limit filter. If your host allows it, you can raise that in wp-config.php, above the line that says to stop editing:
define( 'WP_MAX_MEMORY_LIMIT', 'MEMORY_SIZE' );
MEMORY_SIZE is a value such as 512M. It only works if the host lets PHP raise its own limit; some plans cap it lower regardless. Good result: the same image now uploads. Bad result: the same memory error with the old number, meaning the host cap wins.
The simplest fix is often outside the server: resize photos to around 2560 pixels on the long edge before uploading. No web page needs a 6000 pixel image.
Switch editors as a test
If Imagick fails and GD has enough memory, or the reverse, swapping the order can fix uploads. Create wp-content/mu-plugins/image-editor-order.php with:
<?php
add_filter( 'wp_image_editors', function () {
return array( 'WP_Image_Editor_GD', 'WP_Image_Editor_Imagick' );
} );
There are no placeholders. The wp_image_editors filter sets the order WordPress tries; this puts GD first. Site Health should now show WP_Image_Editor_GD as active. GD usually needs more PHP memory than Imagick and supports fewer formats, so treat this as a test or a stopgap. Delete the file to revert.
Step 3: The big image threshold and Imagick resource limits
big_image_size_threshold
Since WordPress 5.3, when you upload an image wider or taller than 2560 pixels, WordPress creates a scaled copy at 2560 pixels, adds -scaled to its file name, and uses it as the main image. The original stays on disk. The threshold is set by the big_image_size_threshold filter.
The threshold does not prevent processing: WordPress still has to decode the full original to make the scaled copy, so it will not fix a memory error on its own. Change it only for a reason, for example a photography site that needs larger images:
<?php
add_filter( 'big_image_size_threshold', function () {
return PIXEL_LIMIT;
} );
Save it as a file in wp-content/mu-plugins/. PIXEL_LIMIT is the new maximum edge in pixels, such as 4000. Returning false instead disables scaling, so WordPress serves the full original as the main image, which can make pages very heavy. Good result: new uploads above the old limit no longer get a -scaled copy at 2560 pixels.
Imagick resource limits
ImageMagick has its own limits, separate from PHP: memory, map, area, disk, threads and time. Many Linux distributions ship a policy.xml that caps these and can also disable whole formats, often PDF, which stops PDF thumbnails. When an image needs more than the limits allow, Imagick throws an error such as “cache resources exhausted” and the upload fails. Check the limits for the command-line ImageMagick on the server:
magick -list resource
There are no placeholders. On ImageMagick 6 the command is convert -list resource. Good output shows memory and area limits comfortably above what your images need. Very small values, such as a memory limit of 256 MiB with an area limit to match, explain failures on large photos. The PHP extension can use different limits from the command-line tool, so compare with Site Health, Imagick Resource Limits.
Threads matter too. ImageMagick may use several threads per image, which can overload a busy server with many PHP workers. Hosts often set the thread limit to 1, for example with the MAGICK_THREAD_LIMIT environment variable in the PHP-FPM pool. You cannot change policy.xml or the pool on shared hosting; send your host the Site Health values and the log line.
Step 4: “Sorry, you are not allowed to upload this file type”
WordPress only accepts extensions on its allow-list, returned by get_allowed_mime_types() and filtered by upload_mimes. It also checks the file content: wp_check_filetype_and_ext() inspects images and some other files, and rejects a file whose content does not match its extension. A .jpg that is really a HEIC, or a renamed file, can fail even though JPG is allowed.
Confirm it
Check the real type of the file on the server or your computer:
file --mime-type FILE_PATH
FILE_PATH is the file you are trying to upload. Good output is a type that matches the extension, such as image/jpeg for a .jpg. A mismatch, such as image/heic for a .jpg, means the file needs converting, not WordPress settings changed. On multisite, also check Network Admin, Settings, Upload file types, which is a separate list per network.
Add one type safely
If a legitimate type is missing, add only that type in a file in wp-content/mu-plugins/:
<?php
add_filter( 'upload_mimes', function ( $mimes ) {
$mimes['EXTENSION'] = 'MIME_TYPE';
return $mimes;
} );
EXTENSION is the file extension without a dot, such as vtt. MIME_TYPE is its official type, such as text/vtt; use the output of the file command above. If the content check still rejects it, the detected type differs from the one you registered.
ALLOW_UNFILTERED_UPLOADS: why to avoid it
Adding define( 'ALLOW_UNFILTERED_UPLOADS', true ); to wp-config.php lets users with the unfiltered upload capability skip the type check entirely. In core, that means administrators on a single site and only super admins on multisite. It removes the check for every file type, including PHP files. If an administrator account is ever phished or its password reused, an attacker can upload a script and run it. If you use it to get one file in, remove the line immediately afterward. A targeted upload_mimes filter is always the better fix.
SVG files
SVG is not allowed by default for a good reason. An SVG is an XML document that can contain JavaScript. Served from your domain and opened directly, it runs with your site’s origin, which can be used to steal an administrator session. Do not add svg through upload_mimes alone. If you need SVGs, use a plugin that sanitizes each file on upload, restrict SVG uploads to trusted administrators, and keep the sanitizer updated.
Step 5: Folder permissions on uploads/YYYY/MM
“Unable to create directory” and “could not be moved” mean PHP cannot write where WordPress wants to save. Check on the server:
ls -ld wp-content/uploads wp-content/uploads/YYYY wp-content/uploads/YYYY/MM
wp option get upload_path
df -h .
YYYY and MM are the current year and month, the folder named in the error. ls -ld shows each folder’s owner and permissions without listing its contents. Good output shows folders owned by the same user that runs PHP (often the site user) with permissions like drwxr-xr-x (755). Bad output shows root as owner after a restore, or the month folder missing with an unwritable parent. wp option get upload_path should print nothing; a path such as /home/OLD_USER/public_html/wp-content/uploads is left over from a migration and must be cleared with wp option update upload_path "". df -h . shows free disk space; a full disk or quota produces the same errors.
To fix ownership and permissions, run as root only if you must, then hand files back to the site user:
chown -R SITE_USER:SITE_USER wp-content/uploads
find wp-content/uploads -type d -exec chmod 755 {} +
find wp-content/uploads -type f -exec chmod 644 {} +
SITE_USER is the system user that owns the site files and runs PHP; your hosting panel shows it, or run stat -c %U wp-config.php. -R applies the change to everything inside. The find lines set folders to 755 and files to 644. Do not use 777: it lets any user on the server write to your uploads. Good result: the next upload creates the month folder by itself.
“Missing a temporary folder.” means the PHP upload_tmp_dir setting points somewhere that does not exist or is not writable, or the system temporary directory is full. That is a server setting; ask your host.
Step 6: ModSecurity and firewalls blocking uploads
If the Network tab shows a 403 HTML page or 406 for async-upload.php, a web application firewall refused the upload. ModSecurity rules inspect multipart request bodies, and some trigger on file names, on metadata embedded in images, or on body size limits such as SecRequestBodyLimit, which can also return 413. Rename the file to plain lowercase letters and digits and try again; if that works, the file name was the trigger. Otherwise you need the rule ID from the server log. The 403 Forbidden guide explains how to read the block and request a scoped exclusion for async-upload.php.
Advanced: inspect what WordPress actually generated
When an upload “fails” with a processing error, the original is often saved but some sizes are missing. Find the attachment and inspect its metadata on the server:
wp post list --post_type=attachment --orderby=date --order=DESC --posts_per_page=5 --fields=ID,post_title,post_date
wp post meta get ATTACHMENT_ID _wp_attachment_metadata --format=json
ATTACHMENT_ID is the ID from the first command. --posts_per_page=5 shows the five newest uploads. _wp_attachment_metadata holds the dimensions and the list of generated sizes; --format=json prints it in one readable block. Good output lists width, height, a file ending in -scaled for large images, and a sizes object with thumbnail, medium and large. A missing or empty sizes object confirms processing stopped part way. After you fix the cause, rebuild the missing sizes:
wp media regenerate ATTACHMENT_ID --only-missing
--only-missing creates only sizes that do not exist yet, which is faster and gentler on the server. Good output ends with Success: Regenerated 1 of 1 images. An error here repeats the original failure with full text, which is what you send your host.
When to contact your host
Open a ticket when the memory cap cannot be raised, when Imagick limits or policy.xml need changing, when the temporary directory is broken, or when a firewall blocks uploads. Send:
- The exact error text and the UTC time of a failed upload.
- The file type, size in MB and pixel dimensions.
- The PHP error log line, and any 403 response body or rule ID.
- The Media Handling section from Site Health, Info (it has a copy button).
- The output of the
ls -ld and df -h checks if the error mentions a directory.
If the whole site is failing, not just uploads, go to the Emergency Room.