参考:mod_rewrite,URL重写和“漂亮链接”说明


142

“漂亮的链接”是一个经常要求的主题,但是很少对其进行完全解释。mod_rewrite是创建“漂亮链接”的一种方法,但是它很复杂,语法非常简洁,难以理解,并且该文档假定HTTP具有一定的熟练程度。有人可以简单地解释“漂亮链接”如何工作以及如何使用mod_rewrite创建它们吗?

其他通用名称,别名,干净URL的术语:RESTful URL,用户友好的URL,SEO友好的URL,慢速(slugging)和MVC URL(可能是误称)


2
Slug或Slugging是漂亮网址的另一个常见别名/术语。
Mike B

2
@Mike之类的,但通常是漂亮URL 的一部分。例如,当将文章标题转换为URL友好形式,然后将其用作该文章的标识符时,就非常明确。所以reference-mod-rewrite-url-rewriting-explained是蛞蝓,/questions/20563772/reference-mod-rewrite-url-rewriting-explained是漂亮的URL。
deceze

2
我认为应该更新.htaccessmod-rewrite标记,以包括指向该问题的链接,因为它涵盖了定期询问的大部分内容。有什么想法吗?
MikeRockétt'16

Answers:


110

要了解什么是mod_rewrite,首先需要了解Web服务器的工作方式。Web服务器响应HTTP请求。最基本的HTTP请求如下所示:

GET /foo/bar.html HTTP/1.1

这是浏览器对Web服务器的简单请求,从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是请求的URL,则将其视为/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不能神奇地使您的所有URL都“漂亮”。这是一个普遍的误解。如果您的网站中有此链接:

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

mod_rewrite无法做任何使它漂亮的事情。为了使它成为一个漂亮的链接,您必须:

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

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

(一个人可以mod_substitute结合使用来转换传出的HTML页面及其包含的链接。尽管这通常比更新您的HTML资源还要付出更多的努力。)

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

有关所有可能的标志和选项,请参见官方文档


6
也许可以提到2.2.16版中引入的FallbackResource指令,作为重写调度程序的首选方法
Darsstar

78

为了扩展deceze的答案,我想提供一些示例和一些其他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] 是重写规则的标志,以逗号分隔,稍后我将详细说明

上面的重写将允许您链接到类似的东西,/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)或-+(注+以反斜杠转义,因为没有转义,它将作为正则表达式执行重复字符)。可以$2在规则的右侧引用
  • ?表示前面的字符是可选的,所以在这种情况下,两个/blog/1/foo//blog/1/foo将重写到同一个地方
  • $ 表示这是我们要匹配的字符串的结尾

标志

这些选项在重写规则的末尾添加在方括号中,以指定某些条件。同样,您可以在文档中阅读很多不同的标志,但是我将介绍一些更常见的标志:

NC

no case标志表示重写规则不区分大小写,因此对于上面的示例规则,这将意味着/blog/1/foo/和和/BLOG/1/foo/(或此方法的任何变体)都将匹配。

L

最后一个标志指示这是应该处理的最后一条规则。这意味着,当且仅当此规则匹配时,在当前重写处理运行中不会再评估其他规则。如果该规则不匹配,则将照常尝试所有其他规则。如果未设置该L标志,则随后所有以下规则将应用于重写的 URL。

END

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

QSA

查询字符串附加标志允许我们将额外的变量传递到指定的URL,该URL将被添加到原始的get参数中。对于我们的示例,这意味着/blog/1/foo/?comments=15将加载类似/blog/index.php?id=1&title=foo&comments=15

R

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

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]

这是一种非常常见的做法,它将在您的域之前添加www.(如果尚不存在)并执行301重定向。例如,加载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]

这种情况不太常见,但是很好地说明了如果文件名是服务器上存在的目录或文件,则该规则不执行的例子。

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] 将仅对文件扩展名为jpg,jpeg,gif或png(不区分大小写)的文件执行重写。
  • %{REQUEST_FILENAME} !-f 将检查文件是否在当前服务器上,并且仅在不存在时执行重写
  • %{REQUEST_FILENAME} !-d 将检查文件是否在当前服务器上,并且仅在不存在时执行重写
  • 重写将尝试在另一个域上加载相同的文件

39

参考资料

堆栈溢出还有许多其他入门资源:

以及新手友好的正则表达式概述,甚至:

常用的占位符

  • .*匹配任何内容,甚至是空字符串。您不想在所有地方都使用此模式,但通常在最后一个后备规则中使用。
  • [^/]+通常用于路径段。除正斜杠外,它都匹配。
  • \d+ 仅匹配数字字符串。
  • \w+匹配字母数字字符。它基本上是的简写[A-Za-z0-9_]
  • [\w\-]+对于“子弹”式路径段,使用字母,数字,破折号- _
  • [\w\-.,]+添加句号和逗号。\-[…]charclass中更喜欢使用转义的破折号。
  • \.表示原义时期。否则,.[…]任何符号的占位符之外。

这些占位符通常都用(…)括号括起来作为捕获组。而且整个模式经常在^………$开始+结束标记中。引用“模式”是可选的。

重写规则

以下示例以PHP为中心,并且增量更多,更易于适应类似情况。它们只是摘要,通常链接到更多变体或详细的问答。

  • 静态映射
    /contact/about

    将几个页面名称简化为内部文件方案是最简单的:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • 数字标识符
    /object/123

    http://example.com/article/531向现有的PHP脚本等引入快捷方式也很容易。可以将数字占位符重新映射到$_GET参数:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • 子弹型占位符
    /article/with-some-title-slug

    您可以轻松地扩展该规则以允许/article/title-string占位符:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    请注意,您的脚本 必须能够(或适应)将这些标题映射回数据库ID。仅RewriteRules不能凭空创造或猜测信息。

  • 带有数字前缀的子弹
    /readable/123-plus-title

    因此,您经常会看到/article/529-title-slug实践中使用的混合路径:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    现在您title=$2无论如何都可以跳过传递,因为您的脚本通常无论如何都会依赖于数据库ID。在-title-slug已经成为任意URL装饰。

  • 备选清单的一致性
    /foo/… /bar/… /baz/…

    如果您对多个虚拟页面路径具有相似的规则,则可以使用|备用列表进行匹配和压缩。再次将它们重新分配给内部GET参数:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    如果RewriteRule太复杂,可以将它们拆分为单个。

  • 将相关的URL分配到不同的后端
    /date/SWITCH/backend

    替代列表的更实际用途是将请求路径映射到不同的脚本。例如,根据日期为较旧和较新的Web应用程序提供统一的URL:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    这只是将2009-2011年的帖子重新映射到一个脚本,而所有其他年份都隐式地映射到另一个处理程序。请注意,更具体的规则排在第一位。每个脚本可能使用不同的GET参数。

  • /路径斜杠以外的其他定界符
    /user-123-name

    您最常见的情况是看到RewriteRules来模拟虚拟目录结构。但是,您不会被迫独创。您也可以使用-连字符进行分段或结构化。

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    对于同样常见的/wiki:section:Page_Name方案:

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    有时,在- /定界符之间和/ :或甚至.在同一规则中交替也是合适的。或者再次使用两个RewriteRules将变体映射到不同的脚本。

  • 可选的尾部/斜杠
    /dir=/dir/

    选择目录样式的路径时,无论是否使用最终形式,都可以使其到达 /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    现在,这将同时处理http://example.com/blog/123/blog/123/。而且该/?$方法很容易附加到任何其他RewriteRule上。

  • 虚拟路径的灵活细分
    .*/.*/.*/.*

    您将遇到的大多数规则将一组受约束的/…/资源路径段映射到各个GET参数。但是,某些脚本处理可变数量的选项。Apache regexp引擎不允许任意选择它们。但是您可以自己轻松地将其扩展为规则块:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    如果最多需要五个路径段,则将此方案复制到五个规则中。您当然可以分别使用更具体的[^/]+占位符。这里的顺序并不重要,因为两者都不重叠。因此首先拥有最常用的路径是可以的。

    另外,您可以?p[]=$1&p[]=$2&p[]=3在此处通过查询字符串使用PHPs数组参数-如果您的脚本仅希望将它们预分割。(尽管更常见的是只使用包罗万象的规则,然后让脚本本身将这些段扩展到REQUEST_URI之外。)

    另请参阅:如何将URL路径段转换为查询字符串键值对?

  • 可选细分
    prefix/opt?/.*

    一个常见的变化是规则中具有可选的前缀。如果您有静态字符串或更多受约束的占位符,通常这是有意义的:

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    现在,(?:/([^/])+)?那里更复杂的模式只是包装了一个非捕获 (?:…)组,并使其成为可选项)?。包含的占位符([^/]+)将是替换模式$2,但如果没有中间/…/路径,则为空。

  • 捕获剩余的
    /prefix/123-capture/…/*/…whatever…

    如前所述,您通常不需要太通用的重写模式。但是,.*有时将静态比较和特定比较相结合确实有意义。

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    这是可选的所有/…/…/…尾随路径段。然后当然需要处理脚本将其分离,并variabl-IFY提取的参数(这就是Web的“MVC”的框架做)。

  • 尾随文件“扩展名”
    /old/path.HTML

    网址实际上没有文件扩展名。整个引用所针对的是(URL是虚拟定位符,不一定是直接文件系统映像)。但是,如果以前有1:1文件映射,则可以制定更简单的规则:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    其他常见用法是将过期的.html路径重新映射到较新的.php处理程序,或者仅为单独的(实际/实际)文件别名目录名称。

  • 乒乓球(统一重定向和重写)
    /ugly.html←→/pretty

    因此,在某些时候,您要重写HTML页面,使其仅包含漂亮的链接(如deceze概述)。同时,您仍然会收到路径的请求,有时甚至来自书签。至于解决方法,你可以乒乓浏览器显示/建立新网址。

    这个常见的技巧涉及到只要传入URL遵循过时/难看的命名方案,就发送30x / Location 重定向。然后,浏览器将重新请求新的/漂亮的URL,然后将其(仅在内部)重写为原始或新的位置。

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    请注意,此示例仅用于[END]代替[L]安全地进行替代。对于较旧的Apache 2.2版本,除了重新映射查询字符串参数外,您还可以使用其他解决方法: 将丑陋重定向到漂亮的URL,重新映射回丑陋的路径,而没有无限循环

  • 模式中的空格
    /this+that+

    浏览器地址栏中的效果不是很好,但是您可以在URL中使用空格。对于重写模式,请使用反斜杠转义的\␣空格。否则- "引用整个模式或替换:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    客户端使用+或序列化URL %20。但是在RewriteRules中,它们使用所有相对路径段的文字字符进行解释。

经常重复:

普遍存在的.htaccess陷阱

现在把它和​​一粒盐一起吃。并非每条建议都可以推广到所有情况。这只是一些著名的和一些不明显的绊脚石的简单总结:

  • 启用mod_rewrite.htaccess

    要在每个目录的配置文件中实际使用RewriteRules,您必须:

    • 检查您的服务器是否已AllowOverride All启用。否则,您的按目录.htaccess指令将被忽略,并且RewriteRules将不起作用。

    • 显然在模块部分mod_rewrite启用httpd.conf

    • 在每个规则列表前添加RewriteEngine On静止图像。尽管mod_rewrite在<VirtualHost><Directory>部分中是隐式活动的,但每个目录的.htaccess文件都需要单独召唤它。

  • 前导斜杠^/不匹配

    您不应该.htaccess使用以下^/常规方式启动RewriteRule模式:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    这在旧教程中很常见。而且它过去对于古老的Apache 1.x版本是正确的。如今,在RewriteRules中,请求路径相对于目录完全方便的.htaccess。只是离开领导/

    ·请注意,<VirtualHost>尽管前面的斜杠在各节中仍然是正确的。这就是为什么您经常看到它^/?对于规则奇偶校验是可选的。
    ·或者使用时,RewriteCond %{REQUEST_URI}您仍然可以匹配领先者/
    ·另请参阅Webmaster.SE:什么时候在mod_rewrite模式中需要斜杠(/)?

  • <IfModule *> 包装器即将消失!

    您可能已经在许多示例中看​​到了这一点:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • 确实是有意义的<VirtualHost>部分-如果它是另一个后备选项,如ScriptAliasMatch结合。(但是没有人这样做)。
    • 而且它通常用于.htaccess具有许多开源项目的默认规则集。在那里,这只是作为备用广告,并保留“丑陋”的URL作为默认URL。

    但是,您通常不希望在自己的.htaccess文件中使用它。

    • 首先,mod_rewrite不会随机脱离。(如果这样做,您将遇到更大的问题)。
    • 如果确实被禁用,您的RewriteRules仍然不管用。
    • 这是为了防止HTTP 500错误。它通常完成的工作是使用户遭受HTTP 404错误的困扰。(没有那么多的用户更友好,如果你考虑一下吧。)
    • 实际上,它只是抑制更有用的日志条目或服务器通知邮件。你会毫无收获,为什么你从来没有的RewriteRules工作。

    看起来诱人的通用防护措施通常在实践中成为障碍。

  • RewriteBase除非需要,否则不要使用

    许多复制+粘贴示例都包含一个RewriteBase /指令。无论如何,这恰好是隐式默认值。因此,您实际上不需要此。这是一种不错的VirtualHost重写方案的解决方法,并且为某些共享托管者误导了DOCUMENT_ROOT路径。

    在更深的子目录中使用单个Web应用程序很有意义。在这种情况下,它可以缩短RewriteRule模式。通常,最好在每个目录规则集中使用相对路径说明符。

    另请参见RewriteBase如何在.htaccess中工作

  • MultiViews虚拟路径重叠时禁用

    URL重写主要用于支持虚拟传入路径。通常你只需要一个调度程序脚本(index.php)或个别几个处理程序(articles.phpblog.phpwiki.php,...)。后者可能与类似的虚拟RewriteRule路径发生冲突

    一种用于请求/article/123例如可以映射到article.php/123PATH_INFO隐含。您要么必须使用commonplace RewriteCond !-f+ 来保护自己的规则,然后!-d/或者禁用PATH_INFO支持,或者只是禁用Options -MultiViews

    这并不是说您总是必须这样做。内容协商只是虚拟资源的自动化。

  • 订购很重要

    如果尚未,请参阅有关mod_rewrite的所有知识。组合多个RewriteRules通常会导致交互。这并不是每个[L]标志习惯性地防止的东西,而是您一旦熟悉就会采用的方案。您可以重新编写从一条规则到另一条规则的虚拟路径,直到它到达实际的目标处理程序为止。

    不过你会经常想拥有最具体的规则(固定字符串/forum/…模式,或更严格的占位符[^/.]+的)早期规则。泛泛的所有规则(.*)最好留给后面的规则。(例外是将RewriteCond -f/-d守卫作为主要障碍。)

  • 样式表和图像停止工作

    引入虚拟目录结构时,/blog/article/123这会影响HTML中的相对资源引用(例如<img src=mouse.png>)。可以通过以下方法解决:

    • 仅使用服务器绝对引用href="https://stackoverflow.com/old.html"src="/logo.png"
    • 通常,只需将其添加<base href="https://stackoverflow.com/index">到HTML <head>部分即可。这隐式地将相对引用重新绑定到以前的内容。

    您也可以制作其他RewriteRules来重新绑定.css.png将其复制到原始位置。但这都是不必要的,否则会导致额外的重定向并妨碍缓存。

    另请参阅:CSS,JS和图像无法以漂亮的网址显示

  • RewriteConds仅掩盖一个Re​​writeRule

    一个常见的误插是RewriteCond会阻止多个RewriteRules(因为它们在视觉上排列在一起):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    默认情况下不是这样。您可以使用该标志链接它们[S=2]。否则,您将不得不重复它们。尽管有时您可以制定“反向”主要规则,以便尽早[END]进行重写处理。

  • QUERY_STRING免于RewriteRules

    您无法匹配RewriteRule index.php\?x=y,因为默认情况下mod_rewrite仅与相对路径进行比较。您可以通过以下方式分别匹配它们:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    另请参阅如何将查询字符串变量与mod_rewrite匹配?

  • .htaccess<VirtualHost>

    如果在每个目录的配置文件中使用RewriteRules,则担心正则表达式的性能毫无意义。Apache使用通用路由框架将已编译的PCRE模式保留的时间比PHP流程更长。对于高流量站点,一旦经过严格测试,您应该考虑将规则集移入vhost服务器配置。

    在这种情况下,最好使用可选的^/?目录分隔符前缀。这允许在PerDir和服务器配置文件之间自由移动RewriteRules。

  • 每当某事不起作用

    不用。

    • 比较access.logerror.log

      通常,仅查看您error.log和您就可以弄清楚RewriteRule的行为方式access.log。关联访问时间,以查看最初进入哪个请求路径以及Apache无法解析的路径/文件(错误404/500)。

      这并不能告诉您哪个RewriteRule是罪魁祸首。但是,无法访问的最终路径/docroot/21-.itle?index.php可能会放弃进一步检查的地方。否则,请禁用规则,直到获得一些可预测的路径。

    • 启用重写日志

      请参阅Apache RewriteLog文档。要进行调试,您可以在vhost部分中启用它:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      得出有关每个规则如何修改传入请求路径的详细摘要:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      这有助于缩小过于通用的规则和正则表达式的不幸。

      另请参阅:
      · .htaccess不起作用(mod_rewrite)
      · 调试.htaccess重写规则的提示

    • 在问自己的问题之前

      您可能知道,Stack Overflow非常适合在mod_rewrite上提问。 通过包括先前的研究和尝试(避免重复的答案)使它们成为主题,展示基本知识 理解,以及:

      • 包括输入URL的完整示例,错误重写的目标路径以及您的真实目录结构。
      • 完整的RewriteRule集,但可以选择假定的缺陷集。
      • Apache和PHP版本,操作系统类型,文件系统,DOCUMENT_ROOT和PHP $_SERVER环境,如果这与参数不匹配有关。
      • 摘录自access.log和,error.log以验证现有规则解决的问题。更好的是,rewrite.log总结。

      这样可以更快更准确地得出答案,并使它们对其他人更有用。

  • 评论你的 .htaccess

    如果您是从某处复制示例,请小心添加# comment and origin link。尽管忽略归因只是一种不好的举止,但它通常确实会在以后损害维护。记录任何代码或教程源。尤其是当您不熟悉它时,您应该对不将它们像魔术黑匣子那样对待更加感兴趣。

  • 不是“ SEO” -URL

    免责声明:只是一个宠物。您经常听到漂亮的URL重写方案,称为“ SEO”链接或其他内容。尽管这对于谷歌搜索示例很有用,但它已过时。

    没有现代的搜索引擎真正受到路径段.html.php路径段或?id=123查询字符串的干扰。诸如AltaVista之类的旧搜索引擎的确避免了对具有潜在歧义访问路径的网站进行爬网。现代的爬虫通常甚至渴望获得深层的Web资源。

    概念上应使用的“漂亮” URL是使网站变得用户友好

    1. 具有可读且明显的资源方案。
    2. 确保URL是长期有效的(又称为永久链接)。
    3. 通过提供可发现性/common/tree/nesting

    但是,不要牺牲对顺从性的独特要求。

工具类

有多种在线工具可为大多数GET参数网址生成RewriteRules:

通常只输出[^/]+通用占位符,但对于琐碎的站点可能就足够了。


仍然需要一些重写,更多的链接,并且许多子标题有些令人讨厌。这里的其他答案有些重叠,因此也许可以减少。不过,它主要是关于视觉示例以及常见陷阱的列表。
马里奥

3
很久没有看到如此美丽的答案了!阅读时我的眼睛在发光。请不要停止发布这样的答案:)
Rizier123

1
优秀的职位。让我很快了解了mod_rewrite的基本概念!
breez

6

替代mod_rewrite

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

  1. 使用PATH_INFO,卢克

    AcceptPathInfo On默认情况下,如今经常启用。它基本上允许.php和其他资源URL携带虚拟参数:

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

    现在,这/virtual/path在PHP中显示为$_SERVER["PATH_INFO"]您可以在其中处理任何其他自变量的地方。

    这不是作为具有Apache的单独的输入路径段成为方便$1$2$3和将它们作为不同$_GET变量来PHP。它只是以较少的配置工作来模拟“漂亮的URL”。

  2. 启用多视图以隐藏.php扩展名

    还避开.phpURL中的“文件扩展名” 的最简单选项是启用:

    Options +MultiViews
    

    由于匹配的基本名称,这使Apache选择article.phpHTTP请求/article。并与前面提到的PATH_INFO功能一起很好地工作。因此,您可以只使用类似的网址http://example.com/article/virtual/title。如果您有一个带有多个PHP调用点/脚本的传统Web应用程序,那么这才有意义。

    请注意,尽管MultiViews具有不同/更广泛的用途。由于Apache始终会寻找其他具有匹配基名的文件,因此这对性能的影响很小。它实际上意味着内容协商,这样浏览器就接收可用资源中的最佳替代品(如article.en.phparticle.fr.phparticle.jp.mp4)。

  3. 用于无扩展.php脚本的SetType或SetHandler

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

    DefaultType application/x-httpd-php
    

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

    现在这可能会对安全性和性能产生影响,因为所有无扩展名的文件现在都将通过PHP进行管道传输。因此,您可以选择仅对单个文件设置此行为:

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

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

    再次注意,这些设置从一个传播.htaccess到子文件夹。您始终应该为静态资源,上传/目录等禁用脚本执行(SetHandler None和/ Options -Exec或其他php_flag engine off)。

  4. 其他Apache重写方案

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

    • ScriptAliasMatch主要用于CGI脚本,但也应该适用于PHP。它允许正则表达式像任何其他一样RewriteRule。实际上,它可能是配置通用前端控制器的最强大的选项。

    • 平原也Alias有助于一些简单的重写方案。

    • 甚至ErrorDocument可以使用简单的指令来让PHP脚本处理虚拟路径。请注意,这是一种繁琐的解决方法,但是禁止除GET请求外的任何操作,并按定义泛洪error.log。

    有关更多提示,请参见http://httpd.apache.org/docs/2.2/urlmapping.html

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.