I'm trying to only allow incoming requests to /multi
if the Content-Type
request header starts with multipart/form-data
, while requests to other POST
, PUT
, or DELETE
endpoints must always have the application/json
Content-Type
.
When doing the following in my web roots .htaccess
file (no root access):
# Enforce multipart/form-data content type for /multi routes
RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]
# Enforce JSON content type for all other POST, PUT, and DELETE endpoints
RewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE)$
RewriteCond %{HTTP:Content-Type} !^application/json [NC]
RewriteCond %{REQUEST_URI} !^/multi [NC]
RewriteRule ^ - [R=400,L,END]
... and then sending a POST /multi
with Content-Type: multipart/form-data
, I get Apache's 400 Bad Request
Response. When I remove the second rewrite block, the request gets through. Why is the second rewrite block applied when sending a POST /multi
with Content-Type: multipart/form-data
, although I have the RewriteCond %{REQUEST_URI} !^/multi [NC]
condition in it, which should prevent it from being applied?
When I check the apache error logs while sending the request with the second rewrite block enabled, I have no error log at all. When I check the Apache Dom Log, I simply have that an incoming request to /multi
yielded the 400
response. So it indeed seems to be to the fact that the second rewrite rule is applied, which makes no sense to me.
Full .htaccess File
# Prevent files whose name starts with ".ht" from frontend access
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
# Set global Rules for document root directory
Options Indexes FollowSymLinks
Require all granted
# Specify rewrite rules
RewriteEngine On
# Authorization Header seems to be stripped by default, hence explicitly enable it
# Doing it via the rewrite rule is not the native way of doing it, and would need an improved rule to avoid that an
# Authorization HTTP header with an empty value is still passed to PHP
# RewriteCond %{HTTP:Authorization} ^(.*)
# RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
CGIPassAuth On
# Enforce multipart/form-data content type for /multi route
RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]
# Enforce JSON content type for all other POST, PUT, and DELETE endpoints (return a 400 Bad Request otherwise)
RewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE)$
RewriteCond %{HTTP:Content-Type} !^application/json [NC]
RewriteCond %{REQUEST_URI} !^/multi [NC]
RewriteRule ^ - [R=400,L,END]
# Route all incoming requests through index.php, except if it's for existing files
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]
# Ensure that png images, javascript and css files are all delivered with correct mime type. mod_mime must be enabled.
AddType application/javascript .js
AddType image/png .png
AddType text/css .css
# Add an 'Expires' HTTP header for all accessed js, css and png image files. mod_expires must be enabled.
ExpiresActive On
ExpiresByType image/png "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType text/css "access plus 1 year"
# Also add a Cache-Control HTTP header for the same cache lifetime of 1 year, to cover all browsers. mod_headers needed.
<FilesMatch "\.(js|css|png)$">
Header set Cache-Control "public, max-age=31536000"
</FilesMatch>
# 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]
# Ensure that all icons are delivered with the image/jpeg MIME type
RewriteRule ^icons/ - [E=CONTENT_TYPE:image/png]