如何使用 htaccess (RewriteRule) 重定向 URL 并防止直接访问

How to redirect URL with htaccess (RewriteRule) and prevent direct access

使用 .htaccess 我想透明地将对文件夹 "old" 的请求重定向到文件夹 "new",同时阻止直接访问文件夹 "new":

期望的结果:

http://example.com/old/... -> 将显示 "new" 中的内容(在浏览器中没有 URL 更改!) http://example.com/new/... -> 无法访问

这是我在 .htaccess 中的代码(第一行在这里是因为几个域共享同一个根文件夹):

RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^old(.*)$ new [L]
RewriteRule ^new(.*)$ - [F]

好吧,第 3 行触发是因为第 2 行中的替换。我确信标志 "L" 会阻止这种情况发生(处理结束),但似乎并非如此。

您对需要做什么有什么建议吗(我尝试使用重写日志进行调试,但没有成功)?

我做了一些日志记录并发现了以下内容:

[rid#d0ac98/initial] (3) [per-dir C:/www/example/] add path info postfix: C:/www/example/new -> C:/www/example/new/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/new/ -> new/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] applying pattern '^new(.*)$' to uri 'new/'
[rid#d0ac98/initial] (4) RewriteCond: input='localhost' pattern='^example\.com$' => not-matched
[rid#d0ac98/initial] (4) RewriteCond: input='localhost' pattern='^localhost$' => matched
[rid#d0ac98/initial] (2) [per-dir C:/www/example/] rewrite new/ -> old/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] add per-dir prefix: old/ -> C:/www/example/old/
[rid#d0ac98/initial] (2) [per-dir C:/www/example/] strip document_root prefix: C:/www/example/old/ -> /example/old/
[rid#d0ac98/initial] (1) [per-dir C:/www/example/] internal redirect with /example/old/ [INTERNAL REDIRECT]
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/old/ -> old/
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] applying pattern '^new(.*)$' to uri 'old/'
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/old/ -> old/
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] applying pattern '^old(.*)$' to uri 'old/'
[rid#d217a8/initial/redir#1] (2) forcing 'C:/www/example/old/' to be forbidden

这似乎是内部重定向,导致 "forbidden" 结果。事实上,文档中提到了它:

因此,重要的是,如果您在这些上下文之一中使用 RewriteRule 指令,请采取明确的步骤来避免规则循环,而不是仅仅依靠 [L] 标志来终止执行一系列的规则,如下图所示。另一个标志 [END] 不仅可以用于终止当前轮次的重写处理,还可以防止在每个目录 (htaccess) 上下文中发生任何后续重写处理。这不适用于外部重定向产生的新请求...

所以我想在我的示例中错误是由于我使用了 "L" 标志而不是 "END" 标志?

我找到了替代解决方案(此处插入第 3 行):

RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^old(.*)$ new [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^new(.*)$ - [F]

只有在没有内部重定向的情况下才会执行第 4 行。

您可以使用:

RewriteEngine On

# if directly requesting /new then block it
RewriteCond %{THE_REQUEST} \s/+new(/\S*)?\s [NC]
RewriteRule ^ - [F]

# forward /old/abc to /new/abc
RewriteCond %{HTTP_HOST} ^(www\.)?example\.com$ [NC]
RewriteRule ^old(/.*)?$ new [L,NC]

我们在第一条规则中使用了THE_REQUESTTHE_REQUEST 变量表示 Apache 从您的浏览器收到的原始请求,它在执行一些重写规则后不会被覆盖。另一方面,REQUEST_URI 在其他重写规则之后更改其值。

出于同样的原因,您的规则 RewriteRule ^new(.*)$ - [F] 甚至会阻止来自 /old/ 的请求,因为第一条规则将 URI 更改为 /new/