RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]
You have a front-controller pattern later in the .htaccess file that rewrites the request to /index.php. And since this rule only has the L flag (as opposed to END) the rewriting engine starts over with the rewritten URL. (In a .htaccess context the rewrite engine loops until it passes through unchanged).
RewriteCond %{REQUEST_URI} !^/multi [NC]
During the second pass by the rewrite engine the URL is now /index.php (ie. not /multi) so the above condition is successful and the second rule is triggered, sending a 400 response. (The REQUEST_URI server variable is updated between passes by the rewrite engine.)
This "looping" by the rewrite engine is an additional complexity (and "feature") when using mod_rewrite in a directory/.htaccess context. It does not happen (by default) when used in a server (or vitualhost) context (unless you explicitly trigger this behaviour with the PT flag).
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
Aside: It looks like that request would be blocked by the first rule, since you appear to have an erroneous(?) ; at the end of the regex? (ie. a request with Content-Type: multipart/form-data only would satisfy the above negated condition.)
You need to either:
Use the END flag instead on the later rewrite to /index.php (the front-controller), which stops all processing by the rewrite engine. For example:
:
RewriteRule . index.php [END]
(You don't need the slash prefix on the substitution string in the case of an internal rewrite. But you have a RewriteBase directive anyway, which is also not necessary here.)
OR
Instead of using REQUEST_URI in the second rule (which gets updated by the rewrite engine) to test the requested URL, use THE_REQUEST instead, which contains the first line of the request headers and does not get updated. THE_REQUEST would contain a string of the form POST /multi/foo HTTP/1.1. For example, instead of the REQUEST_URI condition, you would use something like the following instead:
# Check the URL as requested by the client, not the rewritten URL
RewriteCond %{THE_REQUEST} !\s/multi
OR
Make sure the second rule only applies to the initial request from the client and not the written request (by the later rule). For this you can add an additional condition (first) and check against the REDIRECT_STATUS environment variable. This is empty on the initial request and set to 200 (as in "200 OK" HTTP status) after the first successful rewrite. For example:
# Only apply this rule to direct requests, not rewritten requests
RewriteCond %{ENV:REDIRECT_STATUS} ^$
:
Other notes:
RewriteRule ^ - [R=400,L,END]
You don't need to specify both L and END. END does the same as L and more. In a .htaccess context L stops the current pass through the rewrite engine but then starts over. Whereas END (new with Apache 2.4) stops the current pass through the rewrite engine and stops any further processing by the rewrite engine.
However, the use of both L and END are unnecessary here. When specifying an R code outside of the 3xx range then processing stops anyway.
RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]
You don't need the condition that checks against the REQUEST_URI server variable (in the first rule) as this check can be performed, more efficiently, in the RewriteRule pattern itself. For example:
RewriteCond %{HTTP:Content-Type} !^multipart/form-data [NC]
RewriteRule ^multi - [NC,R=400]
(Assuming the .htaccess file is in the document root, which comments appear to confirm it is.)
NB: This only applies to the first rule, not the second, which uses a negated expression.
# Define Image Uploads directory, to be able to access img uploads while uploads are kept outside from document root
RewriteRule ^images/(.*)$ /uploads/imgs/$1 [L]
It looks like this rule (near the end of the .htaccess file) is in the wrong place? I'm assuming requests of the form /images/<image> don't map directly to physical files so this rule should go before the front-controller pattern, otherwise it's never going to be processed.
(Nothing is "outside the document root" here, despite what the comment says.)