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.)