In short: post_max_size limits the total size of a POST request, upload_max_filesize the individual file; according to the PHP manual the defaults are 8M and 2M. The values must match the limits in the web server and in the application, otherwise uploads fail without a clear message. Until the PHP2Shell update, php.net advises post_max_size = 8M without uploads or 32M with uploads, upload_max_filesize below that, set so that applications cannot override it.
When the application form suddenly goes silent
After the PHP2Shell interim measures, many admins lowered post_max_size. A few days later the HR department reports that job applications are no longer arriving. The form simply reloads, with no error message. The reason: the request with three PDF attachments was larger than the new limit, and PHP discarded the form data.
The example shows the basic problem. Upload limits act in several places at once, and each place reacts differently when it is exceeded. Anyone who knows the chain can set the values so that they protect without disrupting operations. How PHP processes uploads internally is explained in How PHP processes file uploads; safe programming of upload functions is described in Implementing file uploads in PHP securely.
The chain of upload limits
A request passes through several limits before the application sees it. The following figures come from the Nginx, Apache and PHP documentation:
| Place | Setting | Default per docs | When exceeded |
|---|---|---|---|
| Nginx | client_max_body_size |
1m | error 413 to the browser |
| Apache from 2.4.54 | LimitRequestBody |
1 GiB (up to 2.4.53: unlimited) | error response instead of processing |
| PHP | post_max_size |
8M | $_POST and $_FILES empty |
| PHP | upload_max_filesize |
2M | file is rejected with an error code |
| PHP | max_file_uploads |
20 | further files are ignored |
| PHP | max_input_vars |
1000 | further form fields are missing |
This gives a simple rule: upload_max_filesize smaller than post_max_size, post_max_size smaller than the limit in the web server. The PHP manual also recommends that memory_limit generally be larger than post_max_size. During the PHP2Shell interim measure with memory_limit=-1, the limit via container or cgroup takes over this role; more on this under Setting memory_limit correctly.
post_max_size after PHP2Shell: what php.net advises
In the security announcement of 21 October 2026, php.net names low request limits as one of the interim measures for servers without the update. They reduce the risk of individual attack paths but are expressly not complete protection:
- Without file uploads:
post_max_size = 8M. - With file uploads:
post_max_size = 32M. - In both cases: keep
upload_max_filesizebelowpost_max_size. - Also consider: limits in the web server or proxy for the decoded request size, the number of input fields, the number of multipart parts, the number of header lines and the nesting depth. According to php.net, these limits must also cover requests without a usable
Content-Lengthheader.
The values belong where applications cannot override them. Because post_max_size and upload_max_filesize can be changed at directory level according to the manual, a .user.ini or, with mod_php, an .htaccess could otherwise raise them again:
; FPM pool of the website, with uploads (interim until the update)
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
php_admin_value[max_file_uploads] = 5
In the web server, set the limit just above, so that oversized requests never reach PHP in the first place:
# Nginx, in the server or location block
client_max_body_size 32m;
# Apache, in the VirtualHost: 32 MiB in bytes
LimitRequestBody 33554432
Afterwards reload all PHP workers and the web server. The overview of all interim measures is under Immediate measures if you cannot patch yet. Whether and how upload requests can additionally be filtered at the proxy is examined in Filtering upload requests at the edge.
After every change, test the forms with the largest legitimate requests: applications with several attachments, product imports, image galleries. A value that is too tight paralyzes business processes without anyone seeing an error message.
Determining the right value for your website
Instead of blanket values, a short inventory is worthwhile. List all forms with uploads and note the largest legitimate file and the highest number of simultaneous files. This gives the requirement: upload_max_filesize covers the largest single file, post_max_size the sum of all files in a request plus some reserve for form fields.
If the requirement is above 32M, it does not fit php.net's interim recommendation. That is another reason to bring the update forward. Large uploads can also be handled via a separate subdomain with its own pool, so that the rest of the website keeps tight limits.
After the update to 8.5.12, 8.4.27, 8.3.36, 8.2.35 or a confirmed backport, you can align the values with the actual need again. They should still not be more generous than necessary: every limit that goes beyond the need enlarges the attack surface without benefit. If a website needs no uploads at all, file_uploads = Off is the cleanest solution.
Interpreting error symptoms correctly
When uploads fail, the behavior reveals which limit is acting. A page with error 413 comes from the web server. A form that reloads without a message or reports “invalid request” points to empty $_POST data, that is, post_max_size. A message from the application about a single file fits upload_max_filesize. And missing fields in very large forms, such as with product variants, point to max_input_vars. When post_max_size is exceeded, the PHP error log usually contains a warning with the size of the request.
So that users are not left puzzled, the application should detect the case. The PHP manual suggests a simple way: the form passes a marker in the address, for example ?processed=1. If this marker arrives but $_POST and $_FILES are empty, the request was too large, and the application can show an understandable message with the permitted size.
How does PHP SOS help?
- We record all upload paths of your websites and determine the sizes actually needed.
- We set web server, PHP and pool limits consistently and locked against overriding, including php.net's interim values.
- We test forms, imports and uploads after the changeover and adjust values specifically.
- As part of server hardening, we return the values to the permanent need after the update.
Frequently asked questions
What is the difference between post_max_size and upload_max_filesize?
upload_max_filesize limits the size of a single uploaded file. post_max_size limits the total data of a POST request, that is, all files and form fields together. According to the PHP manual, post_max_size must therefore be larger than upload_max_filesize.
What happens if a request is larger than post_max_size?
According to the PHP manual, $_POST and $_FILES are then empty. The application receives no form data at all and often shows only an unclear error message or reloads the form. This makes the error hard to recognize.
Which values does php.net recommend because of PHP2Shell?
As an interim measure until the update: post_max_size = 8M for applications without file uploads, 32M for applications with uploads, and upload_max_filesize below that. According to php.net this is not complete protection; only the update to a fixed PHP version closes the vulnerabilities.
Can I change post_max_size with ini_set() in code?
No. According to the PHP manual, post_max_size can only be changed at directory level, for example in php.ini, in the FPM pool, in .user.ini or, with mod_php, in .htaccess. To enforce the value, set it with php_admin_value; then even a .user.ini cannot override it.