<-
Apache > HTTP 服务器 > 文档 > 版本 2.5 > Rewrite

使用 mod_rewrite 进行重定向和重映射

可用语言:  de  |  en  |  es  |  fr  |  ja  |  ko  |  tr  |  zh-cn 

本文档是 mod_rewrite 参考文档的补充。它描述了如何使用 mod_rewrite 来重定向和重映射请求。 其中包括许多 mod_rewrite 的常见用法示例, 以及每个示例工作原理的详细描述。

请注意,这些示例中的许多不会在你的特定服务器配置中直接生效, 因此理解它们非常重要,而不仅仅是将示例复制粘贴到你的配置中。

参见

top

从旧到新(内部)

描述:

假设我们最近将页面 foo.html 重命名为 bar.html,现在希望提供旧 URL 以实现向后兼容。 但是,我们希望旧 URL 的用户甚至不会注意到页面已被重命名—— 也就是说,我们不希望浏览器中的地址发生变化。

解决方案:

我们通过以下规则在内部将旧 URL 重写为新 URL:

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "/bar.html" [PT]
top

从旧到新的重写(外部)

描述:

再次假设我们最近将页面 foo.html 重命名为 bar.html,现在希望提供旧 URL 以实现向后兼容。 但这次我们希望旧 URL 的用户能看到新 URL 的提示, 即他们的浏览器地址栏也应该改变。

解决方案:

我们强制进行 HTTP 重定向到新 URL, 这会导致浏览器以及用户视图的变化:

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "bar.html"  [R]
讨论

在本示例中,与上面的内部示例不同, 我们可以简单地使用 Redirect 指令。在前面的示例中使用 mod_rewrite 是为了对客户端隐藏重定向:

Redirect "/foo.html" "/bar.html"
top

资源已移至另一台服务器

描述:

如果资源已移至另一台服务器,你可能希望 URL 在旧服务器上继续工作一段时间,以便人们更新书签。

解决方案:

你可以使用 mod_rewrite 将这些 URL 重定向到新服务器,但也可以考虑使用 Redirect 或 RedirectMatch 指令。

#With mod_rewrite
RewriteEngine on
RewriteRule   "^/docs/(.+)"  "http://new.example.com/docs/$1"  [R,L]
#With RedirectMatch
RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1"
#With Redirect
Redirect "/docs/" "http://new.example.com/docs/"
top

从静态到动态

描述:

如何以无缝的方式(即浏览器/用户不会注意到)将静态页面 foo.html 转换为动态变体 foo.cgi

解决方案:

我们只需将 URL 重写为 CGI 脚本并强制处理程序为 cgi-script,使其作为 CGI 程序执行。 这样,对 /~quux/foo.html 的请求在内部会导致调用 /~quux/foo.cgi

RewriteEngine  on
RewriteBase    "/~quux/"
RewriteRule    "^foo\.html$"  "foo.cgi"  [H=cgi-script]
top

文件扩展名更改的向后兼容性

描述:

在将 document.YYYY 迁移到 document.XXXX(例如将一批 .html 文件翻译为 .php)之后, 如何使 URL 保持向后兼容(仍然虚拟存在)?

解决方案:

仅当具有新扩展名的目标文件存在而具有旧扩展名的原始文件不存在时, 才将 URL 从旧扩展名重写为新扩展名。否则,URL 保持不变。

#   backward compatibility ruleset for
#   rewriting document.html to document.php
#   when and only when document.php exists
<Directory "/var/www/htdocs">
    RewriteEngine on
    RewriteBase   "/var/www/htdocs"

    RewriteCond   "$1.php"           -f
    RewriteCond   "$1.html"          !-f
    RewriteRule   "^(.*).html$"      "$1.php"
</Directory>
讨论

此示例使用了 mod_rewrite 的一个经常被忽视的特性, 利用了规则集的执行顺序。特别是,mod_rewrite 在求值 RewriteCond 指令之前先求值 RewriteRule 的左侧。 因此,在求值 RewriteCond 指令时 $1 已经被定义。 这使我们能够使用相同的基础文件名来测试原始文件 (document.html)和目标文件 (document.php)的存在性。

此规则集设计用于目录级上下文 (在 <Directory> 块或 .htaccess 文件中), 以便 -f 检查正确的目录路径。 你可能需要设置 RewriteBase 指令来指定你正在使用的目录基准。

top

规范主机名

描述:
此规则的目标是强制使用特定的主机名, 而不是可能用来到达同一站点的其他主机名。 例如,如果你希望强制使用 www.example.com 而不是 example.com,你可以使用以下方案的变体。
解决方案:

最佳方式根本不涉及 mod_rewrite, 而是使用放置在非规范主机名虚拟主机中的 Redirect 指令。

<VirtualHost *:80>
  ServerName undesired.example.com
  ServerAlias example.com notthis.example.com

  Redirect "/" "http://www.example.com/"
</VirtualHost>

<VirtualHost *:80>
  ServerName www.example.com
</VirtualHost>

你也可以使用 <If> 指令来完成(2.4 及更高版本):

<If "%{HTTP_HOST} != 'www.example.com'">
    Redirect "/" "http://www.example.com/"
</If>

或者,例如,要将站点的一部分重定向到 HTTPS,你可以执行以下操作:

<If "%{SERVER_PROTOCOL} != 'HTTPS'">
    Redirect "/admin/" "https://www.example.com/admin/"
</If>

如果出于某种原因你仍然想使用 mod_rewrite—— 例如,你需要它与一组更大的 RewriteRule 一起使用—— 你可以使用以下方案之一。

对于运行在非 80 端口上的站点:

RewriteCond "%{HTTP_HOST}"   "!^www\.example\.com" [NC]
RewriteCond "%{HTTP_HOST}"   "!^$"
RewriteCond "%{SERVER_PORT}" "!^80$"
RewriteRule "^/?(.*)"        "http://www.example.com:%{SERVER_PORT}/$1" [L,R,NE]

对于运行在 80 端口上的站点:

RewriteCond "%{HTTP_HOST}"   "!^www\.example\.com"       [NC]
RewriteCond "%{HTTP_HOST}"   "!^$"
RewriteRule "^/?(.*)"        "http://www.example.com/$1" [L,R,NE]

如果你希望对所有域名通用地执行此操作——也就是说, 如果你想将 example.com 重定向到 www.example.com,其中 example.com 可以是任何值, 你可以使用以下方案:

RewriteCond "%{HTTP_HOST}" "!^www\."                    [NC]
RewriteCond "%{HTTP_HOST}" "!^$"
RewriteRule "^/?(.*)"      "http://www.%{HTTP_HOST}/$1" [L,R,NE]

这些规则集可以在主服务器配置文件中使用, 也可以在放置于服务器 DocumentRoot 中的 .htaccess 文件中使用。

top

在多个目录中搜索页面

描述:

某个特定资源可能存在于多个位置, 我们希望在请求时在这些位置中查找该资源。 也许我们最近重新整理了目录结构,将内容分散到多个位置。

解决方案:

以下规则集在两个目录中搜索资源, 如果在两个位置都找不到,则尝试从请求的位置直接提供。

RewriteEngine on

#   first try to find it in dir1/...
#   ...and if found stop and be happy:
RewriteCond         "%{DOCUMENT_ROOT}/dir1/%{REQUEST_URI}"  -f
RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir1/$1"  [L]

#   second try to find it in dir2/...
#   ...and if found stop and be happy:
RewriteCond         "%{DOCUMENT_ROOT}/dir2/%{REQUEST_URI}"  -f
RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir2/$1"  [L]

#   else go on for other Alias or ScriptAlias directives,
#   etc.
RewriteRule "^"     "-"                                          [PT]
top

重定向到地理分布式服务器

描述:

我们的网站有多个镜像, 希望将用户重定向到距其所在国家最近的镜像。

解决方案:

查看请求客户端的主机名,确定他们来自哪个国家。 如果无法查找其 IP 地址,则回退到默认服务器。

我们将使用 RewriteMap 指令来构建我们希望使用的服务器列表。

HostnameLookups on
RewriteEngine on
RewriteMap    multiplex         "txt:/path/to/map.mirrors"
RewriteCond  "%{REMOTE_HOST}"   "([a-z]+)$"                [NC]
RewriteRule  "^/(.*)$"          "${multiplex:%1|http://www.example.com/}$1"  [R,L]

## map.mirrors -- 多路复用映射

de http://www.example.de/
uk http://www.example.uk/
com http://www.example.com/
##EOF##

讨论
此规则集依赖于 HostNameLookups 设置为 on,这可能会对性能产生显著影响。

RewriteCond 捕获请求客户端主机名的最后部分——国家代码——后续的 RewriteRule 使用该值在映射文件中查找适当的镜像主机。

top

规范 URL

描述:

在某些 Web 服务器上,一个资源可能有多个 URL。 通常有规范 URL(即实际使用和分发的 URL) 和只是快捷方式、内部 URL 等的 URL。 无论用户在请求中提供的是哪个 URL, 他们最终应在浏览器地址栏中看到规范 URL。

解决方案:

我们对所有非规范 URL 执行外部 HTTP 重定向, 以在浏览器的地址视图中修正它们,并用于所有后续请求。 在下面的示例规则集中,我们将 /puppies/canines 替换为规范的 /dogs

RewriteRule   "^/(puppies|canines)/(.*)"    "/dogs/$2"  [R]
讨论:
这实际上应该使用 Redirect 或 RedirectMatch 指令来完成:
RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2"
top

移动的 DocumentRoot

描述:

通常 Web 服务器的 DocumentRoot 直接对应 URL "/"。 但这些数据通常不是最高优先级的内容。例如, 你可能希望访客在首次进入站点时转到特定子目录 /about/。 这可以使用以下规则集来实现:

解决方案:

我们将 URL / 重定向到 /about/

RewriteEngine on
RewriteRule   "^/$"  "/about/"  [R]

请注意,这也可以使用 RedirectMatch 指令来处理:

RedirectMatch "^/$" "http://example.com/about/"

还要注意,此示例仅重写根 URL。也就是说, 它重写对 http://example.com/ 的请求, 但不会重写对 http://example.com/page.html 的请求。 如果你确实更改了文档根目录——也就是说,如果你的所有内容 实际上都在该子目录中,那么最好直接更改 DocumentRoot 指令, 或将所有内容向上移动一个目录,而不是重写 URL。

top

回退资源

描述:
你希望单个资源(例如某个文件,如 index.php)处理所有进入特定目录的请求, 但已存在的资源(如图片或 CSS 文件)除外。
解决方案:

从 2.2.16 版本开始,你应该使用 FallbackResource 指令来完成:

<Directory "/var/www/my_blog">
  FallbackResource index.php
</Directory>

但是,在早期版本的 Apache 中,或者如果你的需求比这更复杂, 你可以使用以下重写集的变体来实现相同的目的:

<Directory "/var/www/my_blog">
  RewriteBase "/my_blog"

  RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-f
  RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-d
  RewriteRule "^"                                    "index.php" [PT]
</Directory>

另一方面,如果你希望将请求的 URI 作为查询字符串参数传递给 index.php, 你可以将该 RewriteRule 替换为:

RewriteRule "(.*)" "index.php?$1" [PT,QSA]

请注意,这些规则集可以在 .htaccess 文件中使用, 也可以在 <Directory> 块中使用。

top

重写查询字符串

描述:
你想从查询字符串中捕获特定值,并替换它或将其合并到 URL 的另一个组件中。
解决方案:

本节中的许多解决方案都使用相同的条件, 该条件将匹配的值留在 %2 反向引用中。%1 是查询字符串的开头 (直到感兴趣的键),%3 是剩余部分。 此条件稍微复杂是为了灵活性和避免替换中出现双 '&&'。

  • 此解决方案移除匹配的键和值:
    # Remove mykey=???
    RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
    RewriteRule "(.*)"            "$1?%1%3"
  • 此解决方案在 URL 替换中使用捕获的值, 通过附加 '?' 来丢弃其余的原始查询:
    # Copy from query string to PATH_INFO
    RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
    RewriteRule "(.*)"            "$1/products/%2/?" [PT]
  • 此解决方案在后续条件中检查捕获的值:
    # Capture the value of mykey in the query string
    RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
    RewriteCond "%2"              !=not-so-secret-value
    RewriteRule "(.*)"            "-" [F]
  • 此解决方案展示了前面方案的逆操作, 将路径组件(可能是 PATH_INFO)从 URL 复制到查询字符串中。
    # The desired URL might be /products/kitchen-sink, and the script expects
    # /path?products=kitchen-sink.
    RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]

可用语言:  de  |  en  |  es  |  fr  |  ja  |  ko  |  tr  |  zh-cn