参考:mod_rewrite,URL重写和“漂亮的链接”解释

时间:2013-12-13 10:07:38

标签: apache .htaccess mod-rewrite friendly-url

“漂亮的链接”是一个经常被请求的主题,但它很少被完全解释。 mod_rewrite是制作“漂亮链接”的一种方法,但它很复杂,其语法非常简洁,难以理解,文档假定HTTP具有一定程度的熟练程度。有人能用简单的术语解释“漂亮的链接”是如何工作的,以及如何使用mod_rewrite来创建它们?

其他常见名称,别名,干净网址条款:RESTful网址,用户友好网址,SEO友好网址,Slugging,MVC网址(可能用词不当)

4 个答案:

答案 0 :(得分:98)

要了解什么mod_rewrite,您首先需要了解Web服务器的工作原理。 Web服务器响应HTTP requests。最基本级别的HTTP请求如下所示:

GET /foo/bar.html HTTP/1.1

这是浏览器向Web服务器请求 URL /foo/bar.html的简单请求。重要的是要强调它不会请求文件,它只请求一些任意的URL。请求也可能如下所示:

GET /foo/bar?baz=42 HTTP/1.1

这与URL的请求一样有效,而且显然与文件无关。

Web服务器是一个侦听端口的应用程序,接受来自该端口的HTTP请求并返回响应。 Web服务器完全可以以任何方式响应任何请求,以任何方式配置它以响应。此响应不是文件,而是 HTTP响应,它可能与任何磁盘上的物理文件有关,也可能没有。 Web服务器不一定是Apache,还有许多其他Web服务器,它们都只是持久运行并附加到响应HTTP请求的端口的程序。你可以自己写一个。本段旨在使您与URL直接等于文件的任何概念脱节,这对于理解非常重要。 :)

大多数Web服务器的默认配置是查找与硬盘上的URL匹配的文件。如果服务器的文档根设置为/var/www,则可能会查看文件/var/www/foo/bar.html是否存在并提供服务(如果是)。如果文件以“.php”结尾,它将调用PHP解释器,然后返回结果。所有这些关联都是完全可配置的;一个文件不必以“.php”结尾,以便Web服务器通过PHP解释器运行它,并且URL不必与磁盘上的任何特定文件匹配即可发生。

mod_rewrite是一种重写内部请求处理的方法。当Web服务器收到URL /foo/bar的请求时,您可以将该URL重写为其他内容,然后Web服务器将在磁盘上查找与之匹配的文件。简单的例子:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

当规则与“/ foo / bar”匹配时,此规则会显示,将其重写为“/ foo / baz”。然后将处理请求,就好像已请求/foo/baz一样代替。这可以用于各种效果,例如:

RewriteRule (.*) $1.html

此规则匹配任何内容(.*)和捕获它((..)),然后重写它以附加“.html”。换句话说,如果/foo/bar是请求的网址,则会像处理/foo/bar.html一样处理。有关正则表达式匹配,捕获和替换的详细信息,请参阅http://regular-expressions.info

另一个经常遇到的规则是:

RewriteRule (.*) index.php?url=$1

再次匹配任何内容并将其重写到文件index.php,并在url查询参数中附加最初请求的URL。即,对于进入的任何和所有请求,执行文件index.php,该文件可以访问$_GET['url']中的原始请求,因此它可以随意执行任何操作。

主要是将这些重写规则放入 Web服务器配置文件中。 Apache还允许您将它们放入文档根目录中的.htaccess文件中(即.php文件旁边)。

* 如果主Apache配置文件允许;它是可选的,但通常是启用的。

mod_rewrite 做什么

mod_rewrite并没有神奇地使你的所有网址“漂亮”。这是一种常见的误解。如果您的网站中有此链接:

<a href="/my/ugly/link.php?is=not&amp;very=pretty">

mod_rewrite没有什么可以做的那么漂亮。为了使它成为一个漂亮的链接,你必须:

  1. 将链接更改为漂亮的链接:

    <a href="/my/pretty/link">
    
  2. 使用服务器上的mod_rewrite使用上述任何一种方法处理对URL /my/pretty/link的请求。

  3. (可以将mod_substitute结合使用来转换传出的HTML网页及其包含的链接。虽然这比仅仅更新HTML资源更省力。)

    你可以创建很多mod_rewrite和非常复杂的匹配规则,包括链接几个重写,将请求代理到完全不同的服务或机器,返回特定的HTTP状态代码作为响应,重定向请求等。它非常强大,可以如果你理解了基本的HTTP请求 - 响应机制,那就习惯了。 会自动使您的链接变得漂亮。

    有关所有可能的标记和选项,请参阅official documentation

答案 1 :(得分:73)

为了扩展deceze's answer,我想提供一些其他mod_rewrite功能的示例和解释。

以下所有示例均假设您已在RewriteEngine On文件中加入.htaccess

重写示例

让我们举个例子:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

该规则分为4个部分:

  1. RewriteRule - 启动重写规则
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - 这称为模式,但我只是将其称为规则的左侧 - 您想要重写的内容
  3. blog/index.php?id=$1&title=$2 - 称为替换或重写规则的右侧 - 您要重写的内容
  4. [NC,L,QSA]是重写规则的标志,用逗号分隔,我稍后会详细解释
  5. 上述重写可让您链接到/blog/1/foo/之类的内容,实际上会加载/blog/index.php?id=1&title=foo

    规则的左侧

    • ^表示页面名称的开头 - 因此它会重写example.com/blog/...但不会重写example.com/foo/blog/...
    • 每组(…)括号表示一个正则表达式,我们可以将其捕获为规则右侧的变量。在这个例子中:
      • 第一组括号 - ([0-9]+) - 匹配长度最少为1个字符且仅包含数字值(即0-9)的字符串。这可以通过规则右侧的$1引用
      • 第二组括号匹配长度最少为1个字符的字符串,仅包含字母数字字符(AZ,az或0-9)或-+(注意{{ 1}}使用反斜杠进行转义,因为没有转义,它将作为regex repetition character执行。这可以通过规则右侧的+引用
    • $2表示前面的字符是可选的,因此在这种情况下,?/blog/1/foo/都会重写到同一个地方
    • /blog/1/foo表示这是我们要匹配的字符串的结尾

    标志

    这些是在重写规则末尾的方括号中添加的选项,用于指定特定条件。同样,你可以在the documentation中阅读很多不同的标志,但我会看一些更常见的标志:

    $

    无案例标志意味着重写规则不区分大小写,因此对于上面的示例规则,这意味着NC /blog/1/foo/(或其任何变体)都将匹配。< / p>

    /BLOG/1/foo/

    最后一个标志表示这是应该处理的最后一条规则。这意味着当且仅当此规则匹配时,才会在当前重写处理运行中评估其他规则。如果规则不匹配,则将照常按顺序尝试所有其他规则。如果您未设置L 标记,则以后所有规则都将应用于重写网址。

    L

    自Apache 2.4起,您还可以使用END 标志。与它匹配的规则将完全终止进一步的别名/重写处理。 ([END]标志通常可以触发第二轮,例如重写到子目录或从子目录重写时。)

    [L]

    查询字符串append标志允许我们将额外的变量传递给指定的URL,该URL将被添加到原始的get参数中。对于我们的示例,这意味着QSA 之类的内容会加载/blog/1/foo/?comments=15

    /blog/index.php?id=1&title=foo&comments=15

    这个标志不是我在上面的例子中使用过的,但是我认为值得一提。这允许您指定http重定向,并可选择包含状态代码(例如R )。例如,如果您想在/ myblog / to / blog /上执行301重定向,您只需编写如下规则:

    R=301

    重写条件

    Rewrite conditions使重写更加强大,允许您为更具体的情况指定重写。你可以在the documentation中了解很多条件,但我会谈谈几个常见的例子并解释它们:

    RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]
    

    这是一种非常常见的做法,它会在您的域前添加# if the host doesn't start with www. then add it and redirect RewriteCond %{HTTP_HOST} !^www\. RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] (如果已经存在),并执行301重定向。例如,加载www.会将您重定向到http://example.com/blog/

    http://www.example.com/blog/

    这种情况稍微不那么常见,但如果文件名是服务器上存在的目录或文件,则这是一个不执行的规则的好例子。

    • # if it cant find the image, try find the image on another domain RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*)$ http://www.example.com/$1 [L] 只会对文件扩展名为jpg,jpeg,gif或png(不区分大小写)的文件执行重写。
    • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]将检查当前服务器上是否存在该文件,如果不存在则执行重写
    • %{REQUEST_FILENAME} !-f将检查当前服务器上是否存在该文件,如果不存在则执行重写
    • 重写将尝试在另一个域上加载相同的文件

答案 2 :(得分:5)

mod_rewrite

的替代品

可以在不使用RewriteRules的情况下实现许多基本的虚拟URL方案。 Apache允许在没有.php扩展名和虚拟PATH_INFO参数的情况下调用PHP脚本。

  1. 使用PATH_INFO,Luke

    现在,AcceptPathInfo On通常默认启用。这基本上允许.php和其他资源URL携带虚拟参数:

    http://example.com/script.php/virtual/path
    

    现在这个/virtual/path在PHP中显示为$_SERVER["PATH_INFO"],您可以根据需要处理任何额外的参数。

    将Apache单独的输入路径段分配到$1$2$3并将它们作为不同的$_GET变量传递给PHP,这并不方便。它只是模仿了漂亮的网址&#34;配置工作量减少。

  2. 启用MultiViews隐藏.php扩展名

    最简单的选项还可以避开.php&#34;文件扩展名&#34;在URL中启用:

    Options +MultiViews
    

    由于匹配的基本名称,这为article.php上的HTTP请求选择了/article。这与上述PATH_INFO功能一起使用效果很好。因此,您可以使用http://example.com/article/virtual/title之类的网址。如果您的传统Web应用程序具有多个PHP调用点/脚本,那么这是有意义的。

    请注意,MultiViews具有不同/更广泛的用途。它会导致非常小的性能损失,因为Apache总是查找具有匹配基本名称的其他文件。它实际上适用于Content-Negotiation,因此浏览器会在可用资源中获得最佳替代方案(例如article.en.phparticle.fr.phparticle.jp.mp4)。

    < / LI>
  3. 无扩展.php脚本的SetType或SetHandler

    对于其他文件方案,避免在URL中携带.php个后缀的更直接的方法是configuring the PHP handler。最简单的选项是通过.htaccess覆盖默认的MIME /处理程序类型:

    DefaultType application/x-httpd-php
    

    这样您就可以将article.php脚本重命名为article(不带扩展名),但仍将其作为PHP脚本处理。

    现在这可能会带来一些安全性和性能影响,因为现在所有无扩展名文件都将通过PHP传输。因此,您也可以仅为单个文件设置此行为:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    这在某种程度上取决于您的服务器设置和使用的PHP SAPI。常见的替代方案包括ForceType application/x-httpd-phpAddHandler php5-script

      

    再次注意,此类设置从一个.htaccess传播到子文件夹。您始终应该禁用脚本执行(SetHandler NoneOptions -Execphp_flag engine off等)以获取静态资源,上传/目录等。

  4. 其他Apache重写方案

    在众多选项中,Apache提供了mod_alias个功能 - 有时与mod_rewrite的RewriteRules一样有效。请注意,大多数必须在<VirtualHost>部分中设置,而不是在每个目录.htaccess配置文件中设置。

    • ScriptAliasMatch主要用于CGI脚本,但也适用于PHP。它允许regexp像任何RewriteRule一样。事实上,它可能是配置全能前控制器的最强大的选择。

    • 普通Alias也有一些简单的重写方案。

    • 甚至可以使用普通的ErrorDocument指令让PHP脚本处理虚拟路径。请注意,这是一个麻烦的解决方法,但是,除了GET请求之外什么都禁止,并且根据定义泛滥error.log。

    有关详细提示,请参阅http://httpd.apache.org/docs/2.2/urlmapping.html

答案 3 :(得分:1)

一个关于 URL 重写的常见问题是这样的:

<块引用>

我目前的网址如下所示:

我把它们做成这样:

通过在我的 .htaccess 文件中使用它:

RewriteRule my-blog/(\d+)--i-found-the-answer my-blog/entry.php?id=$1 

但我希望它们看起来像这样:

如何更改我的 .htaccess 文件以使其正常工作?


简单的答案是你不能。

重写规则不会让丑陋的 URL 变得漂亮,而是让漂亮的 URL 变得丑陋

每当您在网络浏览器中输入 URL、点击链接或显示引用图像的页面等时,浏览器都会对特定 URL 发出请求。该请求最终到达网络服务器,网络服务器给出响应

重写规则只是一个规则,它表示“当浏览器请求一个看起来像 X 的 URL 时,给它们相同的响应好像他们要求 Y”。

当我们制定处理“漂亮网址”的规则时,请求漂亮网址,而响应是基于内部丑陋的网址。它不能反过来,因为我们在服务器上编写规则,服务器看到的只是浏览器发送的请求。

你不能使用你没有的信息

鉴于重写规则的作用的这个基本模型,假设您正在向人类发出指令。你可以说:

  • 如果您在请求中看到一个数字,例如“http://example.com/my-blog/42--i-found-the-answer”中的“42”,请将该数字放在"my-blog/entry.php?id="

但如果请求中没有该信息,您的说明将没有任何意义:

  • 如果请求中包含“my-blog”,例如“http://example.com/my-blog/i-found-the-answer”,请在“my-blog”末尾输入正确的数字/entry.php?id="

阅读这些说明的人会说“对不起,我怎么知道正确的数字是多少?”

重定向:“此 URL 当前不在办公室...”

有时,您会看到相反的规则,例如:

RewriteRule my-blog/entry.php?id=(\d+) my-blog/$1--i-found-the-answer [R]

此规则确实匹配左侧的丑陋 URL,并在右侧生成漂亮的 URL。那么我们肯定可以在漂亮部分的开头不写 ID 吗?

RewriteRule my-blog/entry.php?id=(\d+) my-blog/i-found-the-answer [R]

重要的区别在于 [R] 标志,这意味着该规则实际上是一个重定向 - 而不是“提供来自此 URL 的响应”,它的意思是“告诉浏览器改为加载此 URL”。

您可以将此视为其中一封自动电子邮件回复,内容为“抱歉,Joe Bloggs 目前正在休假;请将您的消息发送给 Jane Smith。”同样,上面的重定向告​​诉浏览器“抱歉,http://example.com/my-blog/entry.php?id=42 没有内容;请改为请求 http://example.com/my-blog/42--i-found-the-answer

这个类比的重点是,如果实际上没有叫 Jane Smith 的人在那里工作,或者如果他们不知道如何回答 Joe Bloggs 通常会处理的问题,则上述消息将没有多大用处。同样,如果您告诉浏览器请求的 URL 实际上没有做任何有用的事情,则 重定向 也没有用。一旦浏览器跟随重定向,它就会发出一个新请求,而当服务器收到新请求时,它仍然不知道 ID 号是什么。

但有些网站做到了,所以一定有可能!

网络服务器只包含请求中的信息,但如何使用该信息取决于您。

例如,不是通过 ID 查找博客文章,您可以将其 URL 直接存储在数据库中,然后编写一些代码直接在 PHP、Python、node.js 等中进行匹配。或者您可以使用根据用户在浏览器中设置的语言或基于 cookie 等,相同的 URL 显示不同的内容。

但是你不能在一个 .htaccess 文件中写一行代码来创造奇迹。