.htaccess 将对有或无扩展名的文件的请求规范化为无扩展名的 URL:$1(\.html?) --> $1

.htaccess 将对有或无扩展名的文件的请求规范化为无扩展名的 URL:$1(\.html?) --> $1

这是一个后续问题

  • 我的故意的 .htaccess规则集总体上已实现
  • 请参阅下面引用的该文件的当前形式。

剩余问题:一套规则尚未发挥作用

  • 目标:规范化request(\.htm|\.html)为美丽request
RewriteCond %{REQUEST_URI} ".+\.html?$"
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
RewriteRule (.*)/([^/]+)\.html?$ $1/$2 [R=301]

观察到的行为

  • 请求/hello服务底层/hello.html
    • 这是期望的。在规则中定义2d) DirectoryIndex features implemented in mod_rewrite
  • 请求/hello.html按原样提供。
    • 这是不可取的。
    • 因为提供带后缀和不带后缀的相同文件意味着内容重复和不一致。我想避免这种情况。
    • 因为/hello.htm(l)我想要强制 HTTP 301 重定向到/hello

挑战

  • 正如上面的摘录所示,这个语法看似简单。
  • 但与其他规则集的正确相互作用却不是。尤其是对于2a) Serve file as requested在所有其他情况下仍应起作用的规则。

完整的 .htaccess

## My IA technical concept: All major things work, except one detail.
#
# 1a) Security settings.
# 1b) Explicit requests to index.php CMS router --> Served as-is.
# 2) Static page overlay into CMS namespace
# 2a) /hello — File without file extension exists. --> Served as-is.
# 2b) /hello.html - File with .html extension exists.
#  - Serve in canonical form (=without extension) so at: /hello
#  - Doesn't work yet. This is where I need help.
# 2d) /hello/index.(htm|html|php) — Folder of that name with index file exists.
#  - Note: /hello/ directory listing is explicitly forbidden in Security section (1a)
#  - DirectoryIndex rules cannot "fail gracefully", hence recreated as rules in mod_rewrite. Works fine.
# 3) CMS page exists as cached file --> Cached page gets served at beautiful URL.
# 4) /index.php — If nothing of the above matched hand over to CMS index.php (e.g. Wordpress)

## 1a) Security
### Responding as if those files don't exist
RedirectMatch 404 /\.gitignore
### Directory listing OFF
Options -Indexes
### Valid index files
DirectoryIndex index.html index.htm index.php


## 1b) If CMS router is directly requested, serve as-is
RewriteEngine On
RewriteRule ^index\.php$ - [L]


### 2a) Serve file as requested
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule . - [L]


### 2b) Except if it has a .htm or .html suffix
# - For those we will redirect to the canonical URL with the file extension stripped. --> Not yet working!
# - Serve also filenames without extensions.
#     Those gets served without a content-type (MIME type). --> Works.
#       No problem for those few rare cases as parser of most web browsers detects most important types such as HTML also without a "Content-Type:" header.

# For "/request" check if there's a corresponding "/request.html" and if serve this under "/request"

#### Code block which doesn't work
# - RegEx itself is OK as tested at: https://regex101.com/r/4rTEVk/3
# - Must be a ruleset conflict or order problem
# RewriteCond %{REQUEST_URI} ".+\.html?$"
# RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
# RewriteRule (.*)/([^/]+)\.html?$ $1/$2 [R=301]

#### So in the meanthile I simply serve files with a .htm(l) extension as-is
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule ^([^.]*[^/])$ $1.html [L]
RewriteCond %{DOCUMENT_ROOT}/$1.htm -f
RewriteRule ^([^.]*[^/])$ $1.htm [L]


## 2d) DirectoryIndex features implemented in mod_rewrite
# - index.(htm|html|php) are valid index files
# - /hello/index.(htm|html|php) will get served at /hello

# If a directory is requested, which is missing the trailing slash then append it
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]

# Optimisation: If a directory is not requested then skip the next 3 rules
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . - [S=3]

# NB: Directories end in a trailing slash (enforced above)
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
RewriteRule ^(.+)/$ $1/index.html [L]
RewriteCond %{DOCUMENT_ROOT}/$1/index.htm -f
RewriteRule ^(.+)/$ $1/index.htm [L]
RewriteCond %{DOCUMENT_ROOT}/$1/index.php -f
RewriteRule ^(.+)/$ $1/index.php [L]


## 3) CMS pages cached to files

# BEGIN W3TC Browser Cache
# ... Various rules which set Cache-Control headers per file type.
# ... Machine generated from Admin UI.
# END W3TC Browser Cache

# BEGIN W3TC Page Cache core
# ... Various redirections rules to cached page representations.
# ... Machine generated from Admin UI.
# END W3TC Page Cache core


## 4) Fallback to CMS
# Note that from the default Wordpress .htaccess two conditions (filesystem checks) are removed.
# - The first one that checks for a "file" is handled by multiple of our rulesets.
# - The second check for a "directory" was removed otherwise directories that do not contain a "DirectoryIndex" are not routed to the CMS.

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

答案1

我的 IA/URL 方案:现在完全可以正常工作

1a. 安全设置。

1b. 向/index.phpCMS 路由器发出明确请求 --> 按原样提供。

  1. 静态页面覆盖到 CMS 命名空间

    • 2a) /hello— 存在没有文件扩展名的文件。--> 按原样提供,没有 Content-Type。--> 默认行为。无需规则。
    • 2b) /hello.html- 存在扩展名为 .html 的文件。--> 在/hello(= 无扩展名 = 规范形式) 提供 HTTP 301,内容为/hello.html
    • 2c)/hello请求/hello不存在但/hello.html存在 --> 服务 HTTP 200,/hello内容为/hello.html
    • 2d) /hello/index.(htm|html|php)— 具有索引文件的同名文件夹存在。
      • 注意:/hello/安全部分(1a)明确禁止目录列表
      • DirectoryIndex规则无法“优雅地失败”,因此在 中重新创建规则mod_rewrite。工作正常。
  2. CMS 页面作为缓存文件存在 --> 缓存页面在漂亮的 URL 上提供服务。

  3. /index.php— 如果以上都不匹配,则交给 CMS index.php(例如 Wordpress)

缺失的规则 2b 和 3c 已按如下方式实现

RewriteEngine On

# ...

# Rule 2b: Redirect requests for HTML files to their canonical URL without suffix
RewriteCond %{THE_REQUEST} \s/([^/]+)\.html [NC]
RewriteRule ^ /%1 [R=301,L]

# Rule 2c: Serve HTML files at their canonical URL without suffix
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^/]+)/?$ $1.html [L]

#...

解释所有指令、它们的标志以及如何防止无限循环

规则 2b:将 HTML 文件的请求重定向到不带后缀的规范 URL

RewriteCond %{THE_REQUEST} \s/([^/]+)\.html [NC]
RewriteRule ^ /%1 [R=301,L]
  • RewriteEngine On:该指令启用此.htaccess 文件的重写引擎,允许 URL 重写。
  • RewriteCond %{THE_REQUEST} \s/([^/]+)\.html [NC]:此条件检查当前请求是否包含带后缀的 URL .html%{THE_REQUEST}变量包含浏览器发送到服务器的完整 HTTP 请求行,并且\s/([^/]+)\.html是与 HTML 文件请求匹配的正则表达式。[NC]标志使匹配不区分大小写。
  • RewriteRule ^ /%1 [R=301,L]:此规则匹配任何 URL ( ),并将其重定向到不带后缀 ( )^的 URL 。替换中的 指的是来自前一个 的捕获组。标志表示永久 (301) 重定向,并且这是针对此请求应用的最后一条规则。.html/%1%1RewriteCond[R=301,L]
  • 环路预防机制规则 2b 中的 是隐式的。由于此规则旨在用于重定向,因此无需显式的循环预防措施。该[L]标志可确保此规则是针对给定请求处理的最后一条规则,从而防止后续规则导致循环。

规则 2c - 以不带后缀的规范 URL 提供 HTML 文件

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^/]+)/?$ $1.html [L]
  • 此规则集提供不带.html后缀的 HTML 文件。
  • RewriteCond %{REQUEST_FILENAME} !-f:此条件检查请求的 URL 是否不直接映射到文件系统中的现有文件。如果文件确实存在 ( !-f),则不满足此条件,并跳过该规则。此条件有助于防止循环,因为它确保仅在请求的 URL 与实际文件不对应时才应用规则。
  • RewriteCond %{REQUEST_FILENAME}.html -f:此条件检查附加.html到请求的文件名是否会导致文件系统中存在现有文件。此条件对于防止循环至关重要,因为它确保重写规则仅在相应的 HTML 文件存在时才应用。如果 HTML 文件不存在,则规则将不匹配,从而防止循环。
  • RewriteRule ^([^/]+)/?$ $1.html [L]:此规则使用模式捕获域名后的 URL 部分^([^/]+)/?$并附加到.html该部分。但是,由于上述条件,仅当请求的文件不存在 ( !-f) 且相应的 HTML 文件存在 ( %{REQUEST_FILENAME}.html -f) 时才应用此规则。这可防止服务器不断附加到 URL 的循环.html,因为它仅在必要时应用规则。
  • 环路预防机制是通过使用RewriteCond规则 2c 中的条件 ( ) 来实现的。这些条件确保重写规则仅在必要时应用,从而防止潜在的无限循环。此外,[L]规则 2a 中的标志确保每个请求仅发生一次重定向,从而防止重定向过程中出现循环。

示例请求及其如何通过上述规则进行处理

  1. /sand.html

    • 根据规则 2b 处理请求。HTML 后缀被删除,并向客户端发出 301 重定向到不带后缀的规范 URL /sand
    • 然后,/sand根据规则 2c 处理重写的 URL。服务器内部重写 URL 以提供来自 /sand.html 的内容。
    • 最后,的内容/sand.html就完成了。
  2. /sand

    • 由于该文件存在,因此直接按照规则2c处理该请求/sand.html
    • 服务器在内部重写 URL 来提供来自 的内容/sand.html
    • 的内容/sand.html已送达。

相关内容