URL 掩码对省略尾部斜杠的 URL 不起作用

URL masking not working for URLs that omit the trailing slash

有很多类似的问题,但none似乎很适合我。

我正在从 WordPress 网站转移到简单的静态网站。 但是,我目前被禁止完全删除托管在 public_html 文件夹中的 WordPress 网站,直到一切都被证明可以与静态网站一起使用。

我已将静态站点部署到我的 public_html 文件夹中的子文件夹中,例如/subfolderA/newSiteFolder.

我已经更新 .htaccess 以使用以下内容重定向到子文件夹:

RewriteEngine on
RewriteCond %{REQUEST_URI} !newSiteFolder/ 
RewriteCond %{REQUEST_URI} !subfolderA/newSiteFolder/ 
RewriteRule (.*)$ /subfolderA/newSiteFolder/ [L] 

当从站点内部按 links 导航站点时,这工作正常并在地址栏中正确显示,但是当从外部 link 导航到站点时,子文件夹是显示在地址栏中。

例如,如果从外部点击关于页面 link,它显示为 https://example.com/subfolderA/newSiteFolder/about,而不是 https://example.com/about

从外部 link 单击时,如何屏蔽地址栏中的子文件夹名称?或者如何最好地更改我的重写规则来完成此操作?

我假设 about 实际上是 /subfolderA/newSiteFolder/about 中的物理子目录,并且您打算从中提供 DirectoryIndex 文档(例如 index.html)目录。

“问题”是,当您请求目录时 没有 尾部斜杠 mod_dir 试图通过 301 ( permanent) 重定向,这暴露了内部重写的 file-path.

换句话说,当您请求 /about(没有尾部斜线)时,您的 mod_rewrite 指令在内部将请求重写为 /subfolderA/newSiteFolder/about,但随后 mod_dir 开始并在外部将请求重定向到 /subfolderA/newSiteFolder/about/ 以附加尾部斜杠(这是必需的)。

规范 URL 包含尾部斜杠,这就是您在内部链接的内容。所以我们需要确保在映射到目录时重写的 URL 上始终有一个尾部斜杠。我们可以在重写 URL.

之前 canonical 重定向
RewriteCond %{REQUEST_URI} !newSiteFolder/ 
RewriteCond %{REQUEST_URI} !subfolderA/newSiteFolder/ 
RewriteRule (.*)$ /subfolderA/newSiteFolder/ [L]

第一个条件似乎是多余的。而且,这里使用的正则表达式不是 anchored,所以在请求的 URL-path.

中的任何地方匹配规定的 URL

但是,我们不能只将尾部斜杠附加到所有 URL,因为您可能有静态资源,如 CSS、JS 和图像等。对于任何静态文件,我们不能强制使用尾部斜杠,因此我们需要使用附加规则来处理这个问题。请尝试以下操作:

# Store the base directory in an environment variable
RewriteRule ^ - [E=BASEDIR:/subfolderA/newSiteFolder/]

# Rewrite the root (homepage) only
RewriteRule ^$ %{ENV:BASEDIR} [L]

# Finish early if we are already in the required base directory
RewriteCond %{ENV:BASEDIR}@%{REQUEST_URI} ^([^@]+)@
RewriteRule ^ - [L]

# If the request would map to a directory
#     and it is missing a trailing slash
#     then redirect to append the trailing slash
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}%{ENV:BASEDIR} -d
RewriteRule ^(.+[^/])$ // [R=301,L]

# Rewrite everything to the base directory
RewriteRule (.+) %{ENV:BASEDIR} [L]

以上指令的解释

我选择使用第一条规则将“基本目录”(即 /subfolderA/newSiteFolder/)存储在环境变量 BASEDIR 中,以保存基本目录 file-path 在整个过程中的重复文件。

RewriteCond %{ENV:BASEDIR}@%{REQUEST_URI} ^([^@]+)@

这个条件检查请求的URL(包括重写的URL)是否已经在被重写的基本目录中。 @ 字符只是一个没有出现在 URL-path 中的任意字符,它在正则表达式中没有特殊含义,除了将基本目录 (BASEDIR) 与请求的 URL (REQUEST_URI)。 </code> 是内部反向引用,用于检查所请求的 URL 是否以基目录开头。</p> <blockquote> <pre><code>RewriteCond %{REQUEST_URI} !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}%{ENV:BASEDIR} -d RewriteRule ^(.+[^/])$ // [R=301,L]

第一个 条件 排除任何以 looks-like 文件扩展名结尾的请求(即一个点后跟 2 到 4 个字符),因此我们可以避免更昂贵的目录检查(如下)。这确实假设您没有以 looks-like “文件扩展名”结尾的物理目录。

第二个条件测试请求的URL(例如/about)是否作为目录存在于被重写到的目录中。

正则表达式 ^(.+[^/])$ 匹配(并捕获)任何未以斜杠结尾的 URL-path。

注意:您需要确保在测试之前已清除浏览器缓存,因为之前的 错误 重定向附加了尾部斜线(这也暴露了 file-path ) 是一个 301 永久 重定向,很可能已被浏览器永久缓存。


防止直接访问“隐藏”子目录

Is there a way to also fix the URL for a user who was previously navigated to mydomain/subfolderA/newSiteFolder/about from the external link and saved the link with the subfolders, and is now using that link directly?

您可以阻止直接访问此“隐藏”子目录并将用户重定向回“规范”URL,方法如下。这应该作为上面块中的第三条规则,在“重写根...”规则之后。

# Redirect direct requests to the subdirectory back to root
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{ENV:BASEDIR}@%{REQUEST_URI} ^([^@]+)@(.*)
RewriteRule ^ /%2 [R=301,L]

重要的是,检查 REDIRECT_STATUS env var 的第一个条件排除了后来重写的重写请求,因此此规则仅影响来自客户端的直接请求。

%2 是对前面 CondPattern 中第二个捕获组的反向引用,即。 URL-path BASEDIR.

之后的所有内容

但是,如果用户之前被错误地重定向到子目录,那么这个 redirect 可能会被浏览器缓存,所以上面的重定向删除(撤消)不幸的是,子目录可能会导致这些用户的 redirect-loop,直到他们清除浏览器缓存。 (此 redirect-loop 可能会 提示他们尝试清除浏览器缓存以解决问题;尽管可能不会。)

您或许可以重定向回包含无害查询字符串的 URL。这可能足以防止那些缓存了错误重定向的用户的重定向循环(因为它不是 URL 在他们的缓存中),但它确实会在 URL 上留下多余的查询字符串。比如把上面的RewriteRule指令改成:

:
RewriteRule ^ /%2?noredirect [R=301,L]

noredirect 只是任何查询字符串,以区别于缓存的 URL/redirect.

注意:首先使用 302(临时)重定向进行测试以避免 further/potential 缓存问题。

总结

RewriteEngine On

# Store the base directory in an environment variable
RewriteRule ^ - [E=BASEDIR:/subfolderA/newSiteFolder/]

# Rewrite the root (homepage) only
RewriteRule ^$ %{ENV:BASEDIR} [L]

# Redirect direct requests to the subdirectory back to root
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{ENV:BASEDIR}@%{REQUEST_URI} ^([^@]+)@(.*)
RewriteRule ^ /%2 [R=301,L]

# Finish early if we are already in the required base directory
RewriteCond %{ENV:BASEDIR}@%{REQUEST_URI} ^([^@]+)@
RewriteRule ^ - [L]

# If the request would map to a directory
#     and it is missing a trailing slash
#     then redirect to append the trailing slash
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}%{ENV:BASEDIR} -d
RewriteRule ^(.+[^/])$ // [R=301,L]

# Rewrite everything to the base directory
RewriteRule (.+) %{ENV:BASEDIR} [L]