题 在Apache中重定向,更改URL或将HTTP重定向到HTTPS - 您想知道的关于Mod_Rewrite规则的所有内容但是我们都不敢问


这是一个 典型问题 关于Apache的mod_rewrite。

更改请求URL或将用户重定向到与他们最初请求的URL不同的URL是使用mod_rewrite完成的。这包括以下内容:

  • 将HTTP更改为HTTPS(或其他方式)
  • 将请求更改为不再存在的页面到新的替换。
  • 修改URL格式(例如?id = 3433到/ id / 3433)
  • 基于浏览器,基于月亮和太阳下可能的任何内容,呈现基于浏览器的不同页面。
  • 任何你想要搞乱URL的东西

你想知道的关于Mod_Rewrite规则的一切,但是我不敢问!

如何成为编写mod_rewrite规则的专家?

  • mod_rewrite规则的基本格式和结构是什么?
  • 我需要掌握正则表达式的形式/风格?
  • 编写重写规则时最常见的错误/陷阱是什么?
  • 测试和验证mod_rewrite规则的好方法是什么?
  • 我应该注意mod_rewrite规则的SEO或性能影响吗?
  • 有没有常见的情况,mod_rewrite似乎是工作的正确工具,但不是?
  • 有哪些常见的例子?

一个测试规则的地方

htaccess测试仪 网站是一个玩你的规则并测试它们的好地方。它甚至显示调试输出,因此您可以看到匹配的内容和未匹配的内容。


257
2017-12-20 16:59




这个问题背后的想法是为所有无休止的mod_rewrite问题提供一个接近的路径,这些问题会让我们更多的普通用户发疯。这与子网划分非常相似 serverfault.com/questions/49765/how-does-subnetting-work 。 - Kyle Brandt♦
另外,我真的不想要太多的赞成 题而他们应该回答。我不想CW这个因为我想确保海报得到充分信任我希望是什么 mod_rewrite回答结束所有mod_rewrite问题。 - Kyle Brandt♦
对不起,我赞成了这个问题。 ;-)我真的认为它需要出现在(或接近)顶部 mod-rewrite 标记搜索/过滤器。 - Steven Monday
别人(tm)应该处理常见的用例。我不太清楚它们是否正确。 - sysadmin1138♦
也许这个问题应该链接到mod-rewrite标签wiki,以使路径更短。 - beldaz


答案:


mod_rewrite语法顺序

mod_rewrite有一些影响处理的特定排序规则。在任何事情完成之前, RewriteEngine On 需要给出指令,因为这会打开mod_rewrite处理。这应该在任何其他重写指令之前。

RewriteCond 前 RewriteRule 使一条规则受条件限制。任何后续的RewriteRules都将被处理,就像它们不受条件限制一样。

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html

在这种简单的情况下,如果HTTP引用来自serverfault.com,则将博客请求重定向到特殊的serverfault页面(我们就是这么特别)。但是,如果上面的块有一个额外的RewriteRule行:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html
RewriteRule $/blog/(.*)\.jpg         $/blog/$1.sf.jpg

所有.jpg文件都将转到特殊的serverfault页面,而不仅仅是那些带有引用来自此处的引用者的页面。这显然不是这些规则的编写方式。可以使用多个RewriteCond规则来完成:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

但可能应该使用一些棘手的替换语法。

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

更复杂的RewriteRule包含用于处理的条件。最后一个括号, (html|jpg) 告诉RewriteRule匹配任何一个 html 要么 jpg,并在重写的字符串中将匹配的字符串表示为$ 2。这在逻辑上与前一个块相同,有两个RewriteCond / RewriteRule对,它只在两行而不是四行上完成。

多个RewriteCond行是隐式AND,并且可以显式ORed。要处理来自ServerFault和超级用户的引用者(显式OR):

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)    [OR]
RewriteCond %{HTTP_REFERER}                ^https?://superuser\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

使用Chrome浏览器(隐式AND)提供ServerFault引用页面:

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteCond %{HTTP_USER_AGENT}             ^Mozilla.*Chrome.*$
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

RewriteBase 也是特定于订单的,因为它指定了如何遵循 RewriteRule 指令处理它们的处理。它在.htaccess文件中非常有用。如果使用,它应该是.htaccess文件中“RewriteEngine on”下的第一个指令。举个例子:

RewriteEngine On
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

这告诉mod_rewrite它当前处理的这个特定的URL是通过的方式到达的 http://example.com/blog/ 而不是物理目录路径(/ home / $ Username / public_html / blog)并相应地对待它。正因为如此, RewriteRule 认为它的字符串开头是在URL中的“/ blog”之后。以下是两种不同的写法。一个是RewriteBase,另一个没有:

RewriteEngine On

##Example 1: No RewriteBase##
RewriteCond %{HTTP_REFERER}                                   ^https?://serverfault\.com(/|$)
RewriteRule /home/assdr/public_html/blog/(.*)\.(html|jpg)     $1.sf.$2

##Example 2: With RewriteBase##
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

如你看到的, RewriteBase 允许重写规则利用网络 - 现场 内容的路径而不是网络 - 服务器,这可以使编辑这些文件的人更容易理解。此外,他们可以缩短指令,具有美学吸引力。


RewriteRule匹配语法

RewriteRule本身具有匹配字符串的复杂语法。我将在另一部分中介绍标志(如[PT]之类的内容)。因为系统管理员通过例子比通过阅读更经常地学习 手册页 我将举例并解释他们的所作所为。

RewriteRule ^/blog/(.*)$    /newblog/$1

.* 构造匹配任何单个字符(.)零次或多次(*)。用括号括起来告诉它提供匹配为$ 1变量的字符串。

RewriteRule ^/blog/.*/(.*)$  /newblog/$1

在这种情况下,第一个。*不包含在parens中,因此不会提供给重写的字符串。此规则删除新博客站点上的目录级别。 (/blog/2009/sample.html成为/newblog/sample.html)。

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$2

在这种情况下,第一个括号表达式设置匹配组。这变为$ 1,这是不需要的,因此不会在重写的字符串中使用。

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$1/$2

在这种情况下,我们在重写的字符串中使用$ 1。

RewriteRule ^/blog/(20[0-9][0-9])/(.*)$   /newblog/$1/$2

此规则使用指定字符的特殊括号语法 范围。 [0-9]匹配数字0到9.此特定规则将处理2000年至2099年的年份。

RewriteRule ^/blog/(20[0-9]{2})/(.*)$  /newblog/$1/$2

这与前一个规则的作用相同,但{2}部分告诉它与前一个字符(本例中为括号表达式)匹配两次。

RewriteRule ^/blog/([0-9]{4})/([a-z]*)\.html   /newblog/$1/$2.shtml

这种情况将匹配第二个匹配表达式中的任何小写字母,并尽可能多地填充字符。该 \. construct告诉它将句点视为实际句点,而不是之前示例中的特殊字符。但是,如果文件名中包含破折号,它将会中断。

RewriteRule ^/blog/([0-9]{4})/([-a-z]*)\.html  /newblog/$1/$2.shtml

这会捕获带有破折号的文件名。但是,作为 - 是括号表达式中的特殊字符,它必须是 第一 表达式中的字符。

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

此版本捕获任何带有字母,数字或文件名的文件名 - 文件名中的字符。这是在括号表达式中指定多个字符集的方法。


RewriteRule标志

重写规则上的标志有许多特殊含义和用例

RewriteRule ^/blog/([0-9]{4})/([-a-z]*).\html  /newblog/$1/$2.shtml  [L]

国旗是 [L] 在上面的表达结尾处。可以使用多个标志,用逗号分隔。链接的文档描述了每个文档,但无论如何它们都在这里:

大号 =最后。一旦匹配,就停止处理RewriteRules。订单数量!
C =链。继续处理下一个RewriteRule。如果此规则不匹配,则不会执行下一个规则。稍后会详细介绍。
Ë =设置环境变量。 Apache有各种可能影响Web服务器行为的环境变量。
F =禁止。如果此规则匹配,则返回403-Forbidden错误。
G =走了如果此规则匹配,则返回410-Gone错误。
H =处理程序。强制处理请​​求,就好像它是指定的MIME类型一样。
ñ =下一个。强制规则重新开始并重新匹配。小心!循环可能会导致。
NC =没有案子。允许 jpg 同时匹配jpg和JPG。
NE =没有逃脱。防止将特殊字符(。?#&etc)重写为其十六进制代码等价物。
NS =没有子请求。如果您使用的是服务器端包含,则会阻止与包含的文件匹配。
P =代理人。强制规则由mod_proxy处理。透明地提供来自其他服务器的内容,因为您的Web服务器会获取并重新提供服务。这是一个危险的标志,因为写得不好的人会把你的网络服务器变成一个开放的代理,那就是坏。
PT =通过。考虑RewriteRule匹配中的Alias语句。
QSA = QSAppend。当原始字符串包含查询时(http://example.com/thing?asp=foo)将原始查询字符串附加到重写的字符串。通常它会被丢弃。对动态内容很重要。
[R =重定向。提供指向URL的HTTP重定向。也可以提供精确的重定向代码[R = 303]。非常相似 RedirectMatch,这是更快,应尽可能使用。
小号 =跳过。跳过此规则。
Ť =类型。指定返回内容的mime类型。非常相似 AddType 指示。

你知道我是怎么说的 RewriteCond 仅适用于一条规则吗?好吧,你可以通过链接解决这个问题。

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html     [C]
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

因为第一个RewriteRule具有Chain标志,所以第二个重写规则将在第一个重写规则执行时执行,即当前一个RewriteCond规则匹配时。如果Apache正则表达式会让你的大脑受伤,那就很方便了。但是,从优化的角度来看,我在第一部分中指出的一体化方法更快。

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

这可以通过标志更简单:

RewriteRule ^/blog/([0-9]{4})/([-0-9a-z]*)\.html   /newblog/$1/$2.shtml   [NC]

此外,一些标志也适用于RewriteCond。值得注意的是,NoCase。

RewriteCond %{HTTP_REFERER}        ^https?://serverfault\.com(/|$)     [NC]

将匹配“ServerFault.com”


219
2017-12-20 17:44



做得好。 [填料] - EEAA
非常好 mod_rewrite 和正则表达式底漆。 +1。 - Steven Monday
了解它有时很有用 RewriteCond 实际上是处理过的 后 该 RewriteRule 匹配。您可能希望在顶部附近说“更多关于此内容”,您说“RewriteCond在RewriteRule之前使得一条规则受条件限制。”您可能想提一下,正则表达式是Perl兼容的正则表达式。你还有一个无关的撇号:“... RewriteRule认为它是字符串开始......” - Dennis Williamson
RewriteRule ^/blog/.*/(.*)$ /newblog/$1 不符合 第一 目录组件 - 默认情况下,rewriterules是贪婪的。 /.*/(.*)匹配/ 1 /(2)/和/ 1/2/3/4/5/(6)/,所以你需要/ [^ /] * /只匹配FIRST路径零件。 - adaptr
@sysadmin1138,我认为这个答案很好但是如果你用标志E,N,NS,P,PT和S详细说明它们可能会更好,因为那些标志并不明显它们是如何工作的等等。 - Pacerier


什么是基本格式和   mod_rewrite规则的结构?

我将遵循sysadmin1138关于这些要点的出色答案。

什么形式/风味的常规   表达式我需要有一个坚实的   把握?

除了sysadmin1138概述的语法顺序,语法匹配/正则表达式和RewriteRule标志之外,我相信它还提到mod_rewrite基于HTTP请求头和Apache的配置公开Apache环境变量。

我会推荐 AskApache的mod_rewrite Debug Tutorial 获取mod_rewrite可用的全面变量列表。

什么是最常见的   写重写时的错误/陷阱   规则是什么?

RewriteRule的大多数问题源于对PCRE语法的误解/无法正确转义特殊字符或缺乏对用于匹配的变量内容的深入了解。

典型问题和推荐故障排除:

  • 500内部服务器错误  - 删除Windows托架控件 在配置文件中,如果存在,请确保启用mod_rewrite(包含指令) IfModule 条件是避免这种情况),检查指令语法,注释掉指令直到发现问题
  • 重定向循环  - 使用RewriteLog和RewriteLogLevel,注释掉指令直到发现问题

什么是测试和测试的好方法   验证mod_rewrite规则?

首先,查看您计划匹配的环境变量的内容 - 如果您安装了PHP,这就像将以下块添加到您的应用程序一样简单:

<?php
  var_dump($_SERVER);
?>

...然后编写规则(最好是在开发服务器上进行测试),并记下Apache中任何不一致的匹配或活动 错误日志 文件。

对于更复杂的规则,请使用mod_rewrite RewriteLog 将活动记录到文件和集合的指令 RewriteLogLevel 3

是SEO还是表现   mod_rewrite规则的含义I.   应该知道吗?

AllowOverride all 影响Apache必须检查的服务器性能 .htaccess 每个请求的文件和解析指令 - 如果可能,请将所有指令保留在站点的VirtualHost配置中或启用 .htaccess 仅覆盖需要它们的目录。

谷歌的 网站站长指南 明确说明:“不要欺骗用户或向搜索引擎提供与向用户显示的内容不同的内容,这通常被称为'隐藏'。” - 避免创建过滤搜索引擎机器人的mod_rewrite指令。

搜索引擎机器人更喜欢1:1内容:URI映射(这是对内容链接进行排名的基础) - 如果您使用mod_rewrite创建临时重定向或者您在多个URI下提供相同内容,请考虑指定 规范URI 在HTML文档中。

是否存在常见情况   mod_rewrite似乎是正确的   工作的工具,但不是?

这本身就是一个巨大的(也可能是有争议的)话题 - 更好(恕我直言)根据具体情况处理用途,并让askers确定所建议的决议是否适合他们的需要。

有哪些常见的例子?

AskApache的mod_rewrite技巧和提示 几乎涵盖了定期弹出的每个常见用例,但是,给定用户的“正确”解决方案可能取决于用户配置和现有指令的复杂程度(这就是为什么通常一个好主意 其他 每当出现mod_rewrite问题时,用户都有指令)。


38
2017-12-21 01:00



感谢AskApache链接。这就是我在寻找的东西! - sica07
AskApache小丑官方不受ASF的支持。他说的大部分内容都是值得商榷或明显错误的。 - adaptr
@adaptr请分享您显然知道的优质资源。 - danlefree
“mod_rewrite可能看起来像工作的正确工具,但不是吗?” - 简单 重定向,其中尚未使用mod_rewrite。使用mod_alias Redirect 要么 RedirectMatch 代替。另请参阅Apache文档: 什么时候不使用mod_rewrite - MrWhite


像许多管理员/开发人员一样,我多年来一直在与重写规则的复杂性作斗争,并对现有的Apache文档感到不满,所以我决定作为一个个人项目来深入了解如何 mod_rewrite 实际上它与Apache核心的其余部分一起工作和交互,所以在过去的几个月里,我一直在使用 strace +钻进源代码以获得所有这些的处理。

以下是重写规则开发人员需要考虑的一些关键注释:

  • 重写的某些方面对于服务器配置,虚拟主机,目录,.htaccess处理是常见的 然而
  • 对于根配置(服务器配置,虚拟主机和目录)而言,某些处理与PerDir相反(.htaccess)处理。
  • 更糟糕的是因为PerDir处理几乎可以无差别地触发INTERNAL REDIRECT循环,所以必须写入root配置元素,知道这样的PerDir处理可以触发这一点。

我可以说,因为这样你几乎需要将重写用户社区分成两个类别,并将它们视为完全独立:

  • 那些具有root访问Apache配置的人。这些通常是具有应用程序专用服务器/ VM的管理员/开发人员,这里的消息非常简单:避免使用 .htaccess 文件,如果可能的话;在服务器或vhost配置中执行所有操作。调试很容易,因为开发人员可以设置调试并可以访问rewrite.log文件。

  • 共享托管服务(SHS)的用户

    • 这样的用户  使用 .htaccess / Perdir处理,因为没有其他选择。
    • 更糟糕的是,此类用户的技能水平(就使用mod_rewrite的正则表达式驱动的梯形逻辑而言)通常明显低于有经验的管理员。
    • Apache和托管服务提供商不提供调试/诊断支持。唯一的诊断信息是成功的重定向,重定向到错误的URI。或404/500状态代码。这让他们感到困惑和无助。
    • Apache非常弱,解释了重写如何适用于这个用例。例如,它没有提供PerDir的明确解释 .htaccess选择文件以及原因。它没有解释PerDir循环的复杂性以及如何避免这种情况。

可能还有第三个社区:SHS提供者的管理员和支持人员最终只能在两个阵营中受到影响并且不得不承受上述后果。

我写过一些文章式的博客文章(例如 有关在.htaccess文件中使用重写规则的更多信息)其中包含了很多详细内容,我在此不再赘述这些内容。我有自己的共享服务以及支持一些专用和VM FLOSS项目。我开始使用标准的LAMP VM作为我的SHS帐户的测试工具,但最后我发现做一个合适的镜像VM更好(描述 这里)。

但是,就管理社区应该如何支持而言 .htaccess 用户,我觉得我们需要开发和提供:

  • 关于重写系统如何在PerDir处理中实际工作的连贯描述
  • 关于如何写作的一套指南/最佳实践 .htaccess 重写规则
  • 一个简单的基于Web的重写脚本解析器,类似于W3C html解析器,但用户可以通过它输入测试URI或测试向量,并获得重写逻辑流的立即日志/
  • 有关如何从规则中获取内置诊断的提示(例如

    • 使用 [E=VAR:EXPR] 利用这个事实 EXPR 将扩展反向引用($ N或%N),使其可用作目标脚本的诊断。
    • 如果您使用[OR],[C],[SKIP]和[L]标志局部排序重写规则,以便整个重写方案有效  需要利用内部重定向,然后您可以添加以下规则1,以避免所有循环麻烦:

      RewriteCond %{ENV:REDIRECT_STATUS} !=""
      RewriteRule .  -  [L]
      

21
2018-01-14 16:50



这是有据可查的。为什么你说文档没有解释这个? - adaptr
您所要做的就是订阅 .htaccess 主题,你会看到。大多数初学者都无可救药地混淆了 - 其中大多数都是第一次在共享服务上使用LAMP服务和mod_rewrite,因此无法访问系统/ vhost配置,并且必须使用每个dir处理 .htaccess文件。初学者有一些重要的区别是“流血”。我认为自己是一个超级用户,我仍然在发现细微之处。正如我所说,我不得不使用strace和源代码扫描来解决某些问题。不需要。 :-( - TerryE
我完全同意。 “我们需要将重写用户社区分为两类,并将它们视为完全独立的。”有些用户正在使用共享托管和 需要 依靠 .htaccess即使对于专家来说,这是非常脆弱,复杂和令人困惑的。我仍然遇到麻烦。 - Ryan


使用rewritemap

使用重写映射可以做很多事情。 Rewritemaps使用Rewritemap指令声明,然后可以在RewritCond评估和RewriteRule Subsitutions中使用。

RewriteMap的一般语法是:

RewriteMap MapName MapType:MapSource

例如:

RewriteMap examplemap txt:/path/to/file/map.txt

然后,您可以使用mapname来构造如下:

${examplemap:key}

地图包含键/值对。如果找到密钥,则替换该值。简单映射只是纯文本文件,但您可以使用散列映射,甚至是SQL查询。更多详细信息在文档中:

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritemap

取消字符串。

您可以使用四个内部地图进行一些操作。特别是无法使用的字符串可以派上用场。

例如:我想在查询字符串中测试字符串“café”。但是,浏览器会在将它发送到我的服务器之前将其转义,因此我需要弄清楚URL转义版本对于我希望匹配的每个字符串是什么,或者我可以直接将其取消...

RewriteMap unescape int:unescape

RewriteCond %{QUERY_STRING}  (location|place)=(.*)
RewriteCond ${unescape:%2}   café
RewriteRule ^/find/$         /find/1234? [L,R]

注意我如何使用一个RewriteCond来捕获查询字符串参数的参数,然后使用第二个rewriteCond中的映射来取消它。然后进行比较。 另请注意我需要将%2作为重写映射中的键,因为%1将包含“location”或“place”。当您使用括号对模式进行分组时,它们也会被捕获,您计划使用捕获的结果还是不...


15
2018-04-06 11:57



最后一句话并不完全正确。该 mod_rewrite regexp引擎支持非捕获组,例如 (?:location|place) 这只会在示例中有一个捕获。 - TerryE


什么是最常见的   写重写时的错误/陷阱   规则是什么?

当您重写改变明显路径的URL时,一个非常容易的陷阱,例如:从 /base/1234/index.html 至 /base/script.php?id=1234。客户端将找不到具有脚本位置相对路径的任何图像或CSS。可以找到许多解决此问题的选项 这个常见问题


12
2018-01-01 04:02



谢谢你的链接。特别是在与不熟悉重写的其他团队成员合作时,我发现添加了一个 <base> 标签最容易遵循,仍然启用相对路径。 - kontur