我花了几天时间尝试创建一套特定的规则,以便能够.html
从目录中的所有文件中删除扩展名并呈现更整洁的 URI。我正在使用.htaccess
该网站根目录中的文件,计划在多个存在相同问题的网站中使用它。
我经历过许多类似配置的迭代,但我发现最接近的配置实际上是直接从这里的一篇文章中摘录的(遗憾的是我无法发表评论以了解更多信息)。所以下面是我目前拥有的:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule (.*) $1.html [L]
ErrorDocument 404 https://example.com/404
它很简单,并且在大多数情况下运行良好,但是当存在真正的目录时,它似乎会抛出 403 服务器错误。
例如,如果我访问example.com/directory_A
- ,我会收到 403 错误。但是,根目录中实际上有一个同名的文件,因此我希望它确实会出现example.com/directory_A.html
(但当然没有html
)。directory_A
目录中有一个文件,,file_B.html
访问时会按预期example.com/directory_A/file_B
显示file_B.html
内容。
我正在为此而烦恼——这绝对是我最接近解决问题的一次,但我只是不知道如何帮助我克服这最后的障碍,所以任何帮助都将不胜感激。
答案1
但是当真正的目录存在时,它似乎会抛出 403 服务器错误。
403 不是由您发布的规则引起的。第一个状况无论如何都会特别排除目录,因此甚至不会被处理。
403 是由于 mod_dir 尝试从子目录(可能不存在)提供DirectoryIndex
文档(例如index.html
)而引起的。/directory_A/
具体来说,当您请求/directory_A
(不带尾部斜杠)时,mod_dir 将通过 301(永久)重定向附加尾部斜杠来“修复” URL。然后,在重定向请求中,mod_dir 尝试从该目录提供目录索引,如果不存在且目录列表被禁用(mod_autoindex),则触发 403。
要按要求执行操作,您需要使用该DirectorySlash Off
指令阻止 mod_dir 在物理目录上附加尾部斜杠。然后,为了提供服务/directory_A.html
(而不是经过请求/directory_A
)您需要删除第一个状况排除对目录的请求。
例如:
# Ensure that directory listings are disabled
Options -Indexes
# Prevent mod_dir appending a slash to physical directories
DirectorySlash Off
# Rewrite request to append ".html" extension if it exists
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.*) $1.html [L]
请注意目录列表必须如果您设置了,则必须禁用,DirectorySlash Off
否则,当请求没有尾部斜杠的目录并且相应的文件不存在时,mod_autoindex 将生成目录列表.html
。请注意 Apache 文档中关于DirectorySlash
指示。
在RewriteCond
指令中,我改变了使用REQUEST_URI
反向引用的方式RewriteRule
图案而是保持一致 - 确保在RewriteCond
测试字符串和RewriteRule
代换。
请注意,请求/directory_A/
(带有尾随斜杠)仍会导致 403 响应,但这是预料之中的,除非您特别想处理这种边缘情况并将请求路由到/directory_A.html
?更新:最好的方法是实施外部重定向,当存在相应文件时,只需从 URL 中删除尾部斜杠,.html
这样重写(上述)就可以完成其工作,并.html
在重定向响应中附加扩展名。这可确保您拥有一个单一的规范 URL,避免潜在的重复内容问题(其中/directory_A
和/directory_A/
都返回相同的资源)。
例如立即添加以下“重定向”规则前上述“重写”规则:
# Remove trailing slash on URL-path when the corresponding ".html" file exists
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.*)/$ /$1 [R=302,L]
这不会明确检查目录,因此它也适用于其他“文件”。例如/directory_A/file_B/
将被重定向到/directory_A/file_B
(尾随斜杠被删除)。
首先使用 302(临时)重定向进行测试,只有当您确定它按预期工作时才更改为 301(永久)重定向,以避免潜在的缓存问题。
您需要确保在测试之前清除浏览器缓存,因为 mod_dir 触发的在目录上附加尾部斜杠的早期 301 已经被浏览器缓存。
老实说,最好从一开始就避免此类冲突,并且在实现“无扩展名” URL 时不要使用与物理目录相同的基本名称的文件。
在旁边:
优化
附加扩展的指令.html
可以进行优化,因为它目前正在测试每一个请求是否存在以.html
结尾的文件(这相对昂贵且可能没有必要)。例如请求/images/myimage.jpg
,您的规则将检查文件系统上是否存在/images/myimage.jpg.html
。您可以通过排除已包含文件扩展名的请求来避免这些不必要的检查(假设您的 URL 不会故意在 URL 路径末尾添加看起来像文件扩展名的点)。
例如:
# Rewrite request to append ".html" extension if it exists
RewriteCond $1 !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.*) $1.html [L]
錯誤文檔
ErrorDocument 404 https://example.com/404
这项指令无疑是错误的。
当您指定绝对 URL 时,它将触发错误文档的 302(临时)重定向,而不是内部子请求(这是应该的)。因此,除非您在重定向响应中手动设置,否则客户端不会看到 404 HTTP 状态。但无论哪种方式,客户端都会首先看到 302。
您应该在此处指定 404 错误文档的实际 URL,而不是“无扩展”版本(这需要进行额外处理),正如您在此处所做的那样。这完全是服务器内部的,客户端看不到此 URL。
例如:
ErrorDocument 404 /404.html
尽管通常最好将错误文档放在单独的子目录中,以便于从其他重定向/重写中排除。例如/errordocs/404.html
。