如果没有 RewriteRule 满足请求,.htaccess 将返回错误

如果没有 RewriteRule 满足请求,.htaccess 将返回错误

我正在尝试编写一个简单的 REST API,并且目前正在尝试正确配置我的.htaccess文件。

我想要实现的目标如下:

  • 必须RewriteRule将请求定向到实际的 RestController
  • 捕获所有未明确提供的RewriteRule带有 FITTING 错误的 请求
    • 针对任何文件、子目录或子目录中的文件的请求,未经处理RewriteRule都应该得到答复403
    • 针对任何不存在的文件或子目录的请求都应该得到答复404

我目前所取得的成就是:

  • ✅ 为不存在的文件、子目录以及子目录中的文件提供服务404
  • ✅ 在没有适当的情况下提供现有文件和子目录RewriteRule 403
  • ❌ 将有效的 API 请求转发到正确的端点(在我添加最后一条403规则后它就停止工作了)
  • 403为子目录中的现有文件提供适当的服务RewriteRule- 例如,当我尝试访问时domain.com/subdir/existingfile.pdf,它实际上显示了我不想要的文件。

由于子目录可以动态创建,我希望处理其中的所有子目录和文件 403,而无需手动指定它们。

这是我的当前.htaccess

Options -Indexes
RewriteEngine on
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

# REST mappings
RewriteRule ^json$   RestController.php?key=json [L,nc,qsa]
RewriteRule ^file/(.*)$   RestController.php?key=file&id=$1 [L,nc,qsa]
RewriteRule ^actions$   RestController.php?key=actions&id=none [L,nc,qsa]
RewriteRule ^actions/(.*)$   RestController.php?key=actions&id=$1 [L,nc,qsa]

# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ - [R=404,NC,L]
RewriteRule ^ - [R=403,NC,L]

答案1

因为RestController.php是一个文件,而不是你的“映射”之一,所以最后一条规则将无条件地提供 403。

您需要为 设定例外RestController.php和/或使用END标志(Apache 2.4 中的新功能)而不是 来L阻止对 mod_rewrite 指令的所有进一步处理。(该L标志仅停止当前处理。在目录上下文,例如.htaccess,重写的 URL 被传递到下一个处理阶段,然后在第二阶段处理最后一条规则。)

您也不需要明确的规则来处理 404,因为默认情况下会发生这种情况。只有当请求映射到文件(或目录)时,您才需要一条规则来处理 403。

例如,RestController.php最后一个规则块中有一个例外:

:

# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule !^RestController\.php$ - [R=403]

# A 404 will naturally be served otherwise...

L当使用带有标志的非 3xx 状态时,不需要该标志R

您不一定需要检查目录(第二个条件),除非您碰巧在该目录中有一个DirectoryIndex文档(例如index.phpindex.html)(但您为什么要这样做呢?)。当请求没有文档的目录时,mod_autoindex 会自动提供 403 DirectoryIndex

或者,或者也可以使用END前面规则中的标志:

# REST mappings
RewriteRule ^json$ RestController.php?key=json [END,NC,QSA]
RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA]
RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA]
RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]

# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [R=403]

# A 404 will naturally be served otherwise...

NC这里不需要该标志。还请注意,我删除了$in,^file/(.*)$因为它不是必需的(正则表达式默认是贪婪的)。


在旁边:

# REST mappings
RewriteRule ^json$ RestController.php?key=json [END,NC,QSA]
RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA]
RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA]
RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]

如果您标准化端点,您的“映射”可以简化为一条规则,即始终是keyid参数,并且id可以是空的(不是“无”),无论如何您都需要验证(根据您的规则,/actions/actions/都是有效请求,但导致不同的目标)。然后您在 PHP 脚本中验证目标。

例如:

RewriteRule ^(json|file|actions)(?:/(.*))?$ RestController.php?key=$1&id=$2 [END,NC,QSA]

这确实意味着/json/<foo>/file将被成功重写为RestController.php(否则将被您的原始规则忽略),但这些请求无论如何都应该由您的 REST API 验证来处理。也许.*真的应该是.+

相关内容