Apache 重写导致服务器错误 403,当扩展删除重写后存在真正的目录时

Apache 重写导致服务器错误 403,当扩展删除重写后存在真正的目录时

我花了几天时间尝试创建一套特定的规则,以便能够.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

这项指令无疑是错误的。

  1. 当您指定绝对 URL 时,它将触发错误文档的 302(临时)重定向,而不是内部子请求(这是应该的)。因此,除非您在重定向响应中手动设置,否则客户端不会看到 404 HTTP 状态。但无论哪种方式,客户端都会首先看到 302。

  2. 您应该在此处指定 404 错误文档的实际 URL,而不是“无扩展”版本(这需要进行额外处理),正如您在此处所做的那样。这完全是服务器内部的,客户端看不到此 URL。

例如:

ErrorDocument 404 /404.html

尽管通常最好将错误文档放在单独的子目录中,以便于从其他重定向/重写中排除。例如/errordocs/404.html

相关内容