Troubleshooting

WordPress Maximum Upload Size: Where the Limit Really Lives

The media library says 8 MB, the file is 12 MB, and nothing you edit in WordPress moves the number. It is not a WordPress setting at all: core reads two PHP directives and prints the smaller one. Which limit is really stopping the upload, why post_max_size is usually the culprit, how to raise the limits in the order that works, and why a photograph almost never needs a bigger ceiling.

WordPress Maximum Upload Size: Where the Limit Really Lives

The media library says Maximum upload file size: 8 MB. The file on your desktop is 12 MB. You have read three tutorials, pasted a line into wp-config.php, added a filter to functions.php, cleared the cache, and the number still says 8 MB.

It says 8 MB because that is what PHP told WordPress. The WordPress maximum upload size is not a WordPress setting. Core asks PHP two questions, takes the smaller answer, and prints it.

There are three ceilings involved and the smallest one always wins. The one most people raise is usually not the one stopping them.

Three ceilings, and the smallest one wins

upload_max_filesize is a PHP directive. It caps the size of any single file in a multipart form submission. PHP ships with this set to 2M in its own configuration templates, and hosts commonly raise it to somewhere between 8M and 64M.

post_max_size is also a PHP directive, and it caps the entire request body: every file, plus every form field, plus the multipart boundaries and headers that wrap them. PHP’s own default is 8M. This is the one that quietly ruins things.

The WordPress side is, on a single site, not a limit at all. It is a report. On multisite it becomes a real limit, and that is a genuine exception covered further down.

There is a fourth ceiling on some stacks that has nothing to do with PHP: the web server itself. Nginx enforces client_max_body_size, which defaults to 1 MB, and rejects the request with a 413 before PHP is ever started. If your uploads fail at almost exactly 1 MB and the PHP values look generous, that is your answer, and it lives in the nginx configuration rather than anywhere you can reach from WordPress.

Where the number in the media library comes from

The function is wp_max_upload_size(), it lives in wp-includes/media.php, and it has been in core since WordPress 2.5. It is four lines long. It reads upload_max_filesize, reads post_max_size, converts both from strings like 8M into bytes with wp_convert_hr_to_bytes(), and returns min() of the two through the upload_size_limit filter.

The docblock above it is unusually blunt about what it does: “Determines the maximum upload size allowed in php.ini.” Not allowed by WordPress. Allowed in php.ini.

The string you are staring at is printed in wp-admin/includes/media.php, where the result is passed through size_format() so that 8388608 bytes becomes “8 MB”. The same value is handed to the uploader’s JavaScript as the max_file_size filter in wp_plupload_default_settings(), which is why the browser can refuse a file instantly, before any of it goes over the wire.

So the number is a measurement of your server, taken fresh on every page load. Editing WordPress to change a measurement is like repainting the thermometer. That is the whole reason the settings pages contain no upload size field: there would be nothing for it to write to.

Diagram of the core function wp_max_upload_size showing it read upload_max_filesize and post_max_size, take the minimum, and pass the value through the upload_size_limit filter to the maximum upload file size shown on the media screen.

The post_max_size mistake

This is the single most common way a correct-looking fix does nothing.

Someone sets upload_max_filesize = 64M and stops there. post_max_size is still at its default of 8M. Because core takes the minimum of the two, the media library still says 8 MB. Nothing improved, and the setting that was changed is now completely meaningless, because a 64 MB file can never arrive inside an 8 MB request.

The rule is simple: post_max_size must be larger than upload_max_filesize, not equal to it. The request carries the file plus everything else in the form, so give it real headroom. If you want a 64 MB file to upload, set post_max_size to 80M or 96M. Setting both to 64M works for a bare single file and then fails the moment the request carries anything extra, which is a horrible bug to chase because it looks intermittent.

There is a second, nastier failure mode. When a request body exceeds post_max_size, PHP does not raise a normal upload error. It discards the body and hands the script an empty $_POST and an empty $_FILES. WordPress then has no file and no error code to report, so you get a blank response, a spinner that never finishes, or the generic “HTTP error” in the uploader. That symptom is almost always post_max_size, and it is not what people go looking for, because the message says nothing about size.

The multisite exception

On a network install there really is a WordPress level limit, and it is enforced in PHP rather than merely displayed.

Multisite hooks upload_size_limit_filter() onto the upload_size_limit filter. That function reads the network option fileupload_maxk, which defaults to 1500, multiplies it by KB_IN_BYTES, and returns the minimum of the PHP derived value, that network cap and, unless the network has its space check disabled, the remaining space in the site’s storage quota. A default network therefore caps every upload at about 1.5 MB regardless of how generous the server is.

You change it in Network Admin under Settings, Network Settings, in the Upload Settings block. The field is labelled “Max upload file size” and it is in kilobytes, so 64 MB is 65536, not 64. The neighbouring “Site upload space” field controls the per site quota, and a site that is filling that quota will show a shrinking maximum upload size as the space runs out, which looks like a bug and is not.

None of this applies to a single site install. If you are not running multisite, there is no WordPress setting to find, and every tutorial that tells you to look for one is describing a different kind of install.

Seeing the real numbers instead of guessing

WordPress already shows you all of this. Go to Tools, Site Health, Info, and open the Media Handling section. Core lists the values under labels that are almost readable enough: “Max size of an uploaded file” is upload_max_filesize, “Max size of post data allowed” is post_max_size, and “Max effective file size” is the min() of the two, which is the number the media library prints. The Server section on the same page carries “PHP memory limit” and “PHP time limit”.

If you want the values in front of you while you test uploads, drop this into wp-content/mu-plugins/ as a file called show-upload-limits.php. Create the folder if it does not exist. Must use plugins load automatically and this one only reads and prints, so delete the file when you are done.

<?php
/**
 * Plugin Name: Show upload limits
 * Description: Prints the real PHP limits on the Media screen. Delete when done.
 */
add_action( 'admin_notices', function () {
	$screen = get_current_screen();

	if ( ! $screen || 'upload' !== $screen->id ) {
		return;
	}

	printf(
		'<div class="notice notice-info"><p>upload_max_filesize: %1$s, post_max_size: %2$s, effective: %3$s, memory_limit: %4$s, max_execution_time: %5$s</p></div>',
		esc_html( ini_get( 'upload_max_filesize' ) ),
		esc_html( ini_get( 'post_max_size' ) ),
		esc_html( size_format( wp_max_upload_size() ) ),
		esc_html( ini_get( 'memory_limit' ) ),
		esc_html( ini_get( 'max_execution_time' ) )
	);
} );

One warning about checking these on the command line. Running php -i over SSH, or reading the values through WP-CLI, tells you about the CLI configuration, which is frequently a different php.ini with far more generous limits. Uploads happen in the web SAPI. Trust the browser, not the shell.

Before you raise anything, it is worth knowing which of the three ceilings is the one stopping you, and whether the file will even survive the processing afterwards.

Drop the file below and enter the three values from Site Health. The planner tells you which limit fails first, works out how much memory the image will actually need from its pixel dimensions rather than its file size, and writes the exact lines for php.ini, .user.ini, .htaccess and wp-config.php, with an honest note about which of those your host will ignore.

Upload and memory limit planner

Four separate limits decide whether a file makes it into the media library: upload_max_filesize, post_max_size, memory_limit and max_execution_time. Give this planner the file you want to upload and the four values your server reports, and it works out which limit stops you first and writes the exact lines that fix it. A file you drop is only measured, its size and, for an image, its width and height. Nothing is uploaded and nothing leaves this browser tab.

What you want to upload
Drop a file here
or press Enter to pick one. Only the size is read, and the width and height when it is an image. The file itself stays on your disk.
What the server reports

All four values sit in Tools, Site Health, Info, under Server. The shorthand is the one PHP uses: 64M means 64 times 1024 KB, and -1 means no limit.

Nothing checked yet
LimitThis upload needsThe server allowsVerdict

Where the memory figure comes from

A JPEG does not take up its file size while it is being resized, it takes up its pixel area. GD holds the full bitmap at four bytes per pixel, and it holds the source and the result at the same time while it scales. ImageMagick counts differently and can stream parts of the work, so treat this figure as a rough floor rather than an exact demand.

Where the time figure comes from

A rough estimate at 0.35 seconds per megapixel and per generated size, plus two seconds for the request itself. A fast server beats it, a busy shared host does not. Some hosts also cut a request off at the web server or the proxy, which max_execution_time cannot do anything about.


  

Raise one limit at a time and check Site Health again after each change. If a value refuses to move, the host is setting it somewhere further up, and only support can change it there.

WunderPaint
The Dynamic Design and Automation Studio
WunderPaint is a layered image editor for your WordPress media library. WunderPaint Studio is the same thing in any browser, free and without an account.

Raising the limits, ranked by what actually works

First, the hosting control panel. Almost every managed host exposes these values in a PHP settings screen: cPanel has MultiPHP INI Editor, Plesk has PHP Settings per domain, and most WordPress specific hosts have a plain form or a support macro. This is the correct answer most of the time. It writes to the right file, it survives PHP version upgrades, and it does not leave a stray directive that breaks the site two years later.

Second, php.ini or a user ini file. If you have real server access, edit the php.ini that the web SAPI loads, then restart PHP-FPM. Site Health will not tell you which file that is, so confirm the loaded path with a temporary phpinfo() page or your host’s documentation rather than assuming. If you do not have that access but PHP runs as FastCGI or FPM, you can usually place a .user.ini file in the WordPress root:

upload_max_filesize = 64M
post_max_size = 96M
memory_limit = 256M
max_execution_time = 300

Two things to know about .user.ini. PHP caches it, by default for 300 seconds, so a change may take five minutes to show up and refreshing harder will not help. And it only exists when PHP runs as CGI, FastCGI or FPM. Under mod_php the file is ignored silently, which makes it look like the values are wrong when they were never read.

Third, .htaccess, and only on Apache with mod_php. Above the # BEGIN WordPress block:

php_value upload_max_filesize 64M
php_value post_max_size 96M
php_value memory_limit 256M
php_value max_execution_time 300

If PHP is running through FPM, which is the normal modern arrangement, php_value is not a directive Apache understands and the whole site returns a 500 Internal Server Error. On nginx there is no .htaccess at all and the file is simply never read. Try this one last, keep a way to undo it, and check the front end immediately.

Table comparing the places where PHP upload limits can be changed, showing which work under mod_php and which work under FPM or CGI, alongside a sample user ini file.

The snippets that circulate and do nothing

Search this problem and you will be handed the same three or four pieces of code. Here is why they fail, because the reason matters more than the list.

@ini_set( 'upload_max_filesize', '64M' ); in wp-config.php or in a theme’s functions.php cannot work, and not because of where it is placed. Both upload_max_filesize and post_max_size are marked PHP_INI_PERDIR, which means they can only be set in php.ini, .htaccess, .user.ini or the web server configuration. A running script cannot change them. The leading @ suppresses the warning that would have told you so. This is the purest cargo cult in the genre: it looks like configuration, it is syntactically valid, and it has no effect whatsoever. If you are wondering where such code would belong if it did work, the answer is still not functions.php, but in this case there is no correct location, because the mechanism does not exist.

Filtering upload_size_limit to return a bigger number is worse than useless, because it appears to work. The media library dutifully prints your new figure and the JavaScript uploader stops blocking large files, so you get further than before. Then PHP throws the file away at the door exactly as it did previously, and now you have a screen that lies to everyone who uses it. On single sites, leave that filter alone.

Setting define( 'WP_MEMORY_LIMIT', '256M' ); is real code that really works, but it has nothing to do with upload size. It raises how much memory WordPress will ask PHP for, which matters after the upload succeeds, not during it. People add it while chasing an upload limit, see nothing change, and conclude the fix was wrong when it was simply aimed elsewhere.

Finally, a plugin that “increases the maximum upload size” is usually doing one of the above on your behalf, most often the upload_size_limit filter. A few genuinely attempt to write a .user.ini or .htaccess for you, which is a real mechanism, but you can write those two files yourself in less time than the plugin takes to install.

What breaks once the big file gets in

Raising the ceiling only moves the failure. A 40 MB JPEG that uploads successfully now has to be processed, and that is where the next two limits bite.

memory_limit is the one that kills large photographs. GD decompresses an image into raw pixels before it can touch it, and the file size on disk tells you almost nothing about how much RAM that takes. An 8000 by 6000 pixel photograph is 48 million pixels, and at four bytes per pixel that is roughly 190 MB of raw bitmap for the source alone, before the resized copy exists alongside it. This is why the upload bar reaches 100 percent and then the screen returns a blank error: the transfer worked and the resize ran out of memory. There are three separate memory ceilings in a WordPress install, and it is worth knowing which one applies during a media upload.

max_execution_time, commonly 30 seconds, is the other one. WordPress does not make one copy of your upload. It makes every registered image size, which on a typical theme is several files, plus the -scaled version core creates automatically once an image exceeds the big_image_size_threshold of 2560 pixels on its longest side. One upload becomes a lot of files, and each one is a separate encode. Big source images can genuinely exceed half a minute of processing.

Raise both alongside the upload limits, in the same place, at the same time. A 64 MB upload limit sitting next to a 128 MB memory limit is a trap you built yourself.

Statistics on the memory and time cost of resizing a large photograph, with a table of the failure points from the nginx body limit through post_max_size to memory_limit and max_execution_time.

The 12 MB photograph is the real problem

Everything above assumes you are right to want the bigger limit. Sometimes you are. Video, PDFs, a theme zip, a client’s press pack: those need headroom, and the fix is a genuinely higher ceiling.

For photographs, it is nearly always the wrong fix. A 12 MB phone photo is 12 MB because it is several thousand pixels wide and encoded at near maximum quality for editing, not for the web. Your content area is perhaps 800 pixels wide, and on a high density screen you serve maybe 1600. Everything above that is thrown away by the browser after being decoded. Core’s -scaled behaviour already caps the version it serves at 2560 pixels, so the original is stored, backed up, migrated and never delivered to a single visitor.

Resize before uploading, to something like 2000 or 2560 pixels on the long edge, and re-encode at a sane quality. The same picture typically lands in the hundreds of kilobytes, uploads instantly under an 8 MB limit, never troubles memory_limit, and looks identical on the page. Choosing the right format compounds that: WebP at quality 80 will usually beat a JPEG of matching visual quality by a comfortable margin, and WordPress has supported WebP in core since 5.8.

If the oversized originals are already sitting in your media library, resizing them one at a time is not realistic, which is exactly the case WunderPaint’s batch processor exists for.

Symptom and cause

The number in the media library never changes, whatever you edit in WordPress. It is min( upload_max_filesize, post_max_size ) read from PHP on every page load. WordPress has no stored value to update. Change PHP, not WordPress.

You raised upload_max_filesize to 64M and the number still says 8 MB. post_max_size is still 8M and core takes the smaller of the two. Raise post_max_size above it, with headroom.

The uploader shows a blank or generic “HTTP error” with no size mentioned. The request body exceeded post_max_size, so PHP discarded it and handed WordPress an empty $_FILES. There is no error for core to report, which is why the message is unhelpful.

The progress bar reaches 100 percent, then it fails. The transfer finished and the processing did not. That is memory_limit during the resize, or max_execution_time while generating every registered size.

Uploads fail at roughly 1 MB no matter what PHP says. Nginx’s client_max_body_size is still at its 1 MB default and returns a 413 before PHP runs. It has to be fixed in the server configuration.

The maximum is about 1.5 MB and this is a network install. That is the fileupload_maxk network option at its default of 1500 kilobytes. Network Admin, Settings, Upload Settings.

The limit dropped on its own, on multisite. The site is near its upload space quota, and core reports the remaining space as the maximum upload size.

Adding php_value lines to .htaccess took the whole site down. PHP is running as FPM or CGI, where Apache does not recognise php_value. Remove the lines and use .user.ini instead.

What to take away

The reason this problem is so frustrating is that it presents itself inside WordPress, in the WordPress admin, in WordPress’s own wording, while being entirely a property of the server underneath. Four lines of core, in place since WordPress 2.5, read two PHP directives and print the smaller one. Every hour spent editing WordPress files is spent on the wrong side of that boundary.

Once you accept that, the procedure is short. Read the real values in Site Health. Raise both directives together in the control panel if you have one, in php.ini or .user.ini if you do not, and treat .htaccess as a last resort you can undo quickly. Raise memory_limit and max_execution_time in the same edit, because a file that gets in and then dies during resizing is not progress. Then check the media library again, because that number is the only honest confirmation that any of it worked.

And before you raise anything, look at what you are uploading. If it is a photograph, the limit is not the problem. A 12 MB image on a web page is a mistake whether or not it fits through the door, and the version your visitors receive was going to be a fraction of that size anyway. The best outcome of this whole investigation is usually that you stop needing a bigger limit.

WordPress Maximum Upload Size: Where the Limit Really Lives

Table of Contents

Learn it by building something

Every week one thing you can make the same afternoon, from dynamic templates to 3D type. Written down step by step.

One mail a week, and then it ends.
Unsubscribe in one click.

Photo Editing

Turn a phone photo of a document into something that looks scanned

A photographed page is tilted, tapered, grey and shadowed, and all three faults are completely determined. Four corners give eight equations, a heavy blur estimates the lighting so it can be divided out, and Otsu picks the threshold. No model, no download, and it finishes before you let go of the button.

Photo Editing

Fix White Balance: The One-Click Correction, Explained

A colour cast is a wrong assumption applied evenly, which means it can be divided back out. Click something that was neutral, read the three channel factors, and understand exactly where a global correction stops working.

Photo Editing

Repair a photo that messengers and re-saves have turned to blocks

JPEG damage is not random. It is made in blocks of eight by eight pixels, in a known order, which is why a model trained on compressed images beats any amount of sharpening. It runs in your browser, repairs at four times the size and hands the result back clean.

Developer Tools

Favicon Generator: The Icon Files a Site Actually Needs

The favicon is the one image on your site nobody checks at the size everybody sees. Here is the short list of files a site still needs, the two measurements that decide whether your mark survives a browser tab and an Android mask, and exactly where the WordPress site icon stops.

WordPress Images

Image Quality Curve: Why Every Recommended Setting Is Wrong

Quality 80 means one thing in JPEG, another in WebP, and something different again on a screenshot than on a photograph. Here is what the number actually sets, what SSIM can and cannot tell you, and how to find the point where your own file stops getting meaningfully smaller and starts getting visibly worse.

SEO & Structured Data

Redirect Rule Generator: The Rules That Never Fire

A redirect list rarely breaks. It accumulates: a broad prefix rule that kills every rule below it, a target missing a trailing slash that doubles your hop count, a 301 where a 308 was needed. Generate the Apache, nginx and CSV versions of your rules, then walk a URL through them and watch where it actually goes.

Download the free WunderPaint Plugin for WordPress

The WunderPaint workspace with the layers panel, adjustment sliders, text style presets and the asset library along the bottom

The Image Editor & Design Studio

Everything described here can be done in the browser, on your own site. The live demo runs the full editor with nothing to install.

Free

Chaos Art

Autonomous painters make one-of-a-kind abstract art in 3D space - gestures, art movements, painterly media, and embeds that paint a new original for every visitor.

Pro

Particle Strokes

Paint with swarms of light: twenty-two movements, a stamp you draw yourself, and curves that give a stroke a shape - the swarm keeps painting for a few seconds after you let go.

Pro

City Diorama

Any place on earth as a miniature you could hold: real streets, water and building footprints raised into a 3D diorama - or wrapped around a sphere as your own tiny planet.

Free

Papercut Art

Layered paper pictures with real depth - a photo sliced along its actual depth into up to twenty layers, parametric landscapes, animals and clouds you can shape, letters with real counters, and a look that runs on three dials.

Pro

3D Earth Studio

A hyperrealistic globe - day and night with city lights, live clouds, atmosphere halo, country borders and highlights, click-to-place markers with flight-route arcs, satellite orbits, seamless rotation video and a live website embed.

Free

Mystic Studio

Turn a birth date into wall art - a real natal chart with houses and aspects, the moon of that night, zodiac and Chinese zodiac posters, numerology cards and a synastry wheel for two, in eight artful themes.

Free

Marble Bath

Marble paper on a virtual water bath - drop, rake and comb real Ebru patterns with gestures, flowers and classic recipes, razor-sharp at any size and re-editable as a layer.

Free

Day Ring

Turn a day into a beautiful circular schedule - colour-coded time blocks as arcs around a 24-hour clock, with concentric rings for overlaps, emoji, templates and a legend.

Free

Code Shot

Turn code into a gorgeous, share-ready image - syntax highlighting, editor themes, window frames and diff highlighting - then drop it into your design as a re-editable layer.

Pro

3D Solar System Studio

Build a date-accurate 3D solar system - real planet positions for any date, photoreal textures and one slider from artistic to true scale - then drop it into your design as an editable layer.

Pro

3D Molecule Studio

Build a real 3D molecule - from a curated library, the periodic table or a SMILES string - then style it, measure it and drop it into your design as an editable layer.

Pro

3D Textile Studio

Drop your design onto cloth that behaves like the material you pick: silk falls soft, felt holds its shape, flag fabric snaps in the wind. Hang it, blow it and drape it, then lay the finished drape back into your document as a picture.

Pro

3D Particle Studio

Point the engine at any layer and it becomes a cloud of particles that keeps its colours, flowing through a sphere, a galaxy or your own outline. Keep the frame you like as a still, or embed the running engine so it keeps moving on your page.

Free

Origami

Put your own picture on the paper and watch that very sheet fold itself into a crane or a box. Every step is a station you can stop at and turn around in 3D, which is exactly where printed diagrams leave you alone.

Pro

3D Flip Studio

A hardcover you can leaf through, a limp magazine, a strewn pile of sheets, a sticker peeling off its backing. The curl is real geometry, so the print never slides across the paper.

Free

Handwriting Fonts

Draw the alphabet here or fill in a printed sheet and photograph it. What comes out is a genuine font family, installed into your site and available in every picker.

Pro

Step Guides

Turn any picture into an instruction. Every mark is pinned to a place in the image, so arrows still point at the right thing after the callout has been dragged somewhere else.

Free

Text Art

One studio, sixteen art types: ASCII and emoji art, brick, dice, cube, sticky-note, LED, ceramic and keycap mosaics, word portraits, text flows, silhouettes, element tiles and more.

Free

Photo Mosaic

The classic photomosaic, computed in your browser: your main image emerges from many media-library photos via true structure matching - never a cheap overlay.

Free

Puzzle Sheets

Generate printable puzzle sheets: word search, mazes in eight shapes, sudoku with unique solutions, criss-cross, cryptograms and number pyramids.