In short: Filtering upload requests at the edge means rejecting POST requests of type multipart/form-data at the proxy, web server or in the WAF if they do not go to a known upload path. Trade authors suggest this as a temporary, unofficial measure against PHP2Shell. It can break forms and uploads and never replaces the update. Proceed in three steps: log, define exceptions, only then block, with a fixed end date.
A suggestion from the expert discussion
Until a server is patched, operators look for anything that lowers the risk. In the expert discussion about PHP2Shell, one suggestion came up: allow POST requests in the format multipart/form-data, the format for file uploads and some forms, only where the application really expects them. All others are rejected at the proxy or in the web application firewall.
The classification is important. The suggestion does not come from php.net but from trade authors, and it is expressly intended as a temporary measure. php.net names its own interim measures, collected under Immediate measures if you cannot patch yet. The filter can complement these, not replace them.
Here we describe how to introduce it in a controlled way. We deliberately leave out details of why it might help against which attacks.
What the filter achieves and where it harms
The idea behind it is a reduction of the attack surface. Most pages of a website need no file uploads. If only a few known paths still accept such requests, a large part of possible attack attempts no longer reaches PHP at all.
The downside is just as clear. Every function that sends multipart requests and is missing from the exception list fails. This affects not only obvious upload forms.
| Function | Often uses multipart/form-data | Risk when blocking |
|---|---|---|
| Contact and application forms with attachments | yes | requests get lost |
| Media management in the CMS backend | yes | editors cannot upload images |
| Import of products or customers | yes | interfaces break silently |
| Forms without attachments | sometimes | depends on the form builder |
| Interfaces and callbacks of external services | rarely, but possible | failure is noticed late |
There is also a fundamental problem: Cloudflare, which has itself rolled out protection rules against PHP2Shell, warns that rules based on the request data alone are not enough. How to think the two levels together is described in WAF or update: why you need both.
Step 1: log first, then block
Before you block anything, find out which paths receive multipart requests at all. The standard logs do not show the content type. For Nginx, therefore, set up a separate log temporarily:
# in the http block
map $uri $upload_pfad {
default 0;
~^/kontakt/upload\.php$ 1;
~^/admin/medien/ 1;
}
map "$request_method:$upload_pfad:$content_type" $multipart_fremd {
default 0;
"~*^POST:0:multipart/form-data" 1;
}
log_format multipart '$time_iso8601 $remote_addr "$request" "$content_type" $status';
# in the server block
# Caution: an access_log in the server block replaces the inherited log,
# so specify the normal log again here.
access_log /var/log/nginx/access.log combined;
access_log /var/log/nginx/multipart-test.log multipart if=$multipart_fremd;
The entries in $upload_pfad are examples. Start with an empty exception list, then the test log shows all multipart POSTs. Under Apache the same works with an environment variable:
# in the VirtualHost
SetEnvIfNoCase Content-Type "^multipart/form-data" ist_multipart
CustomLog ${APACHE_LOG_DIR}/multipart-test.log combined env=ist_multipart
Let the log run for a few days, ideally over a period in which editors, applicants and interfaces were active as well.
Step 2: define exceptions and block in a controlled way
From the test log, derive the paths that legitimately need uploads. Compare the list with the people responsible: who maintains content, which forms exist, which services send data?
- Create the exception list from the log and inquiries and enter it in
$upload_pfador theRewriteCondlines. - Activate the filter first in a test environment or in a quiet time window.
- Play through all forms, uploads and order paths yourself.
- Watch the error log for rejected requests (status 403) and extend the list if needed.
- Set an end date: after the PHP update and the restart of all workers, the filter is removed again.
In Nginx, add the block for this in the server block:
# in the server block, after evaluating the logging phase
if ($multipart_fremd) {
return 403;
}
In Apache, mod_rewrite takes over the task. According to the Apache documentation, multiple conditions are combined with AND:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} =POST
RewriteCond %{HTTP:Content-Type} ^multipart/form-data [NC]
RewriteCond %{REQUEST_URI} !^/kontakt/upload\.php$
RewriteCond %{REQUEST_URI} !^/admin/medien/
RewriteRule ^ - [F]
If you use a CDN or a cloud WAF, the same logic can often be mapped as a rule of its own. There too: first in logging mode, then block.
The filter is no substitute for the PHP update and no reason to postpone it. PHP2Shell is fixed only with PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 or a confirmed backport, followed by a restart of all PHP processes.
Putting the measure in context
The upload filter belongs in a series with other suggestions currently circulating. Some are sensible, others harmful. An overview of which tips trade authors reject, such as blocking all requests with a session cookie, is given in Dangerous bad tips on PHP2Shell.
How PHP processes uploads internally and why the multipart/form-data format plays a special role is explained in How PHP processes file uploads. For most companies, however, the most important step is not the filter but a clear date for the update.
Conclusion: a tool for days, not months
The upload filter at the edge is a typical example of an emergency measure: it can reduce the attack surface during the interim period, but has side effects and no official backing from php.net. Used properly, with a logging phase, a coordinated exception list, tests and a fixed end date, it is defensible. Without these steps you risk lost applications, blocked editors and silent interface errors.
So make the decision consciously and record who made it and when. And keep the order in mind: first comes the PHP update with a restart of all workers, php.net's interim measures bridge the time until then, and the filter is at most an additional layer.
How does PHP SOS help?
- Assess the situation: We clarify whether an upload filter makes sense at all in your situation or whether the update is possible faster.
- Log and evaluate: We set up the logging phase and derive the exception list from real accesses.
- Block in a controlled way: We activate the filter, test forms and order paths and monitor rejected requests.
- Remove cleanly: As part of WAF and virtual patching, we remove the filter again after the update.
Frequently asked questions
Is filtering multipart requests an official recommendation from php.net?
No. php.net names other interim measures, such as limits for post_max_size and disabling session.upload_progress. Filtering at the edge is a suggestion from trade articles and is expressly intended only as a temporary measure.
Which functions can fail because of the filter?
Everything that sends files or forms in multipart format and is not on your exception list: contact forms with attachments, application forms, media management in the CMS, import functions, some interfaces and payment callbacks. That is why a logging phase comes before blocking.
Does the filter replace the PHP update?
No. At most it reduces the attack surface during the interim period. Cloudflare itself points out that rules based on the request data alone are not enough. PHP2Shell is fixed only with a patched PHP version and the restart of all workers.
Should I block requests with PHPSESSID instead?
No. Trade authors expressly reject this circulating tip. It paralyzes logins and shopping carts without offering reliable protection.