我尝试仅允许请求标头/multi
以Content-Type
开头的传入请求multipart/form-data
,而对其他POST
、PUT
或DELETE
端点的请求必须始终具有application/json
Content-Type
.
在我的网络根文件中执行以下操作时.htaccess
(无根访问权限):
# 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]
...然后发送POST /multi
with Content-Type: multipart/form-data
,我得到 Apache 的400 Bad Request
响应。当我删除第二个重写块时,请求就会通过。为什么在发送POST /multi
with时应用第二个重写块Content-Type: multipart/form-data
,尽管我有条件RewriteCond %{REQUEST_URI} !^/multi [NC]
,这应该阻止它被应用?
当我在启用第二个重写块的情况下发送请求时检查 apache 错误日志时,我根本没有错误日志。当我检查 Apache Dom 日志时,我只是收到了一个传入请求以/multi
产生400
响应。因此,似乎确实应用了第二个重写规则,这对我来说没有意义。
完整的 .htaccess 文件
# 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]
文件后面有一个前端控制器模式
.htaccess
,它将请求重写为/index.php
. 由于此规则仅具有L
标志(与 相对END
),因此重写引擎会使用重写的 URL 重新开始。(在.htaccess
上下文中,重写引擎会循环,直到它保持不变)。在重写引擎第二次传递期间,URL 现在是
/index.php
(即不是/multi
),因此上述条件成功,并且触发第二条规则,发送 400 响应。(REQUEST_URI
服务器变量在重写引擎的传递之间更新。)在目录/上下文中使用 mod_rewrite 时,重写引擎的这种“循环”会带来额外的复杂性(和“功能”)
.htaccess
。当在服务器(或vitualhost )上下文中使用时,它不会发生(默认情况下)(除非您使用标志显式触发此行为PT
)。旁白:看起来该请求将被第一条规则阻止,因为您
;
在正则表达式的末尾似乎有一个错误(?)?(即只有一个请求Content-Type: multipart/form-data
才能满足上述否定条件。)您需要:
END
在稍后重写/index.php
(前端控制器)时使用该标志,这会停止重写引擎的所有处理。例如:(在内部重写的情况下,您不需要在替换字符串上使用斜杠前缀。但
RewriteBase
无论如何您都有一个指令,这在这里也不是必需的。)或者
不要
REQUEST_URI
在第二条规则(由重写引擎更新)中使用来测试请求的 URL,THE_REQUEST
而是使用包含请求标头的第一行且不会更新的规则。THE_REQUEST
将包含以下形式的字符串POST /multi/foo HTTP/1.1
。例如,REQUEST_URI
您可以使用如下所示的内容来代替条件:或者
确保第二条规则仅适用于客户的初始请求,而不适用于书面请求(根据后面的规则)。为此,您可以添加一个附加条件(首先)并检查
REDIRECT_STATUS
环境变量。该值在初始请求时为空200
,并在第一次成功重写后设置为(如“200 OK”HTTP 状态)。例如:其他注意事项:
您不需要同时指定
L
和END
。END
做同样的事情,L
甚至更多。在.htaccess
上下文中L
,停止当前通过重写引擎的传递,但然后重新开始。而END
(Apache 2.4 中的新功能)会停止当前通过重写引擎的传递,并停止重写引擎的任何进一步处理。然而,这里不需要同时使用
L
和。END
当指定R
3xx 范围之外的代码时,处理无论如何都会停止。您不需要检查服务器变量的条件(在第一条规则中),因为可以在模式
REQUEST_URI
本身中更有效地执行此检查。例如:RewriteRule
(假设该
.htaccess
文件位于文档根目录中,这些注释似乎证实了它的存在。)注意:这仅适用于第一条规则,不适用于第二条规则,第二条规则使用否定表达式。
看起来这条规则(靠近文件末尾
.htaccess
)位置错误?我假设表单的请求/images/<image>
不会直接映射到物理文件,因此该规则应该位于前端控制器模式之前,否则它永远不会被处理。(不管评论怎么说,这里没有什么是“文档根目录之外的”。)