Reference: mod_rewrite,URL 重写和 “pretty links” 解释

“pretty links” 是一个经常被要求的话题,但它很少被充分解释。mod_rewrite是制作“漂亮链接”的一种方法,但它很复杂,语法非常简洁,难以理解,而且文档假定对 HTTP 有一定的熟练程度。有人能简单地解释一下“漂亮的链接”是如何工作的,以及如何使用 mod _ rewrite 来创建它们吗?

其他常见的名称,别名,干净的 URL 术语: RESTful URL,用户友好的 URL,SEO友好的 URL,重击和 MVC URL (可能是用词不当)

45510 次浏览

要理解什么是 mod _ rewrite,首先需要理解 Web 服务器是如何工作的。Web 服务器响应 < em > HTTP 请求 。HTTP 请求在最基本的层次上看起来是这样的:

GET /foo/bar.html HTTP/1.1

这是浏览器向 Web 服务器发出的一个简单请求,请求它提供 网址 /foo/bar.html。重要的是要强调,它并没有请求 文件,它只是请求一些任意的 URL。请求也可以是这样的:

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

这就像对 URL 的请求一样有效,而且更明显的是,它与文件没有任何关系。

Web 服务器是一个监听端口的应用程序,它接受来自该端口的 HTTP 请求并返回响应。Web 服务器可以完全自由地以它认为合适的任何方式响应任何请求/以您已经配置的任何方式响应任何请求。这个响应不是一个文件,而是一个 HTTP 响应,它可能与任何磁盘上的物理文件有关,也可能与物理文件无关。网络服务器不一定是 Apache,还有许多其他的网络服务器,它们都是持久运行的程序,并附加到一个端口来响应 HTTP 请求。你可以自己写。这一段的目的是让您脱离 URL 直接等同于文件的任何概念,理解这一点非常重要。:)

大多数 Web 服务器的默认配置是查找与硬盘上的 URL 匹配的文件。如果服务器的 文件根目录被设置为,比如说,/var/www,那么它可以查看文件 /var/www/foo/bar.html是否存在,如果存在,则为其提供服务。如果文件以”结尾。它将调用 PHP 解释器,那么返回结果。所有这些关联都是完全可配置的; 文件不必以”结尾。网络服务器通过 PHP 解释器运行它,并且 URL 不需要匹配磁盘上的任何特定文件就可以发生某些事情。

Mod _ rewrite 是 重写内部请求处理的一种方法。当网络服务器收到一个请求的 URL /foo/bar,你可以 重写该网址到别的东西之前,网络服务器将在磁盘上寻找一个文件来匹配它。举个简单的例子:

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="/my/ugly/link.php?is=not&amp;very=pretty">

Mod _ rewrite 不能做任何事情来使这个链接更漂亮。为了使这个链接更漂亮,你必须:

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

    <a href="/my/pretty/link">
    
  2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.

(One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)

There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.

See the official documentation for all the possible flags and options.

为了扩展 Deceze 的回答,我想提供一些示例和其他一些 mod _ rewrite 功能的解释。

下面的所有示例都假设您已经在 .htaccess文件中包含了 RewriteEngine On

重写示例

让我们举个例子:

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个字符,只包含字母数字字符(A-Z、 A-Z 或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

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

R

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

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将检查文件是否存在于当前服务器上,如果不存在,则只执行重写
  • 重写将尝试在另一个域上加载相同的文件

Mod _ rewrite 的替代方案

许多基本的虚拟 URL 方案可以在不使用 RewriteRules 的情况下实现。Apache 允许使用虚拟 PATH_INFO参数调用 PHP 脚本,而不需要 .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扩展名

    在 URL 中避免使用 .php“文件扩展名”的最简单选项是启用:

    Options +MultiViews
    

    由于匹配的基名,这使得 Apache 为 /article上的 HTTP 请求选择 article.php。这与前面提到的 PATH _ INFO 特性一起工作得很好。所以你可以使用像 http://example.com/article/virtual/title这样的 URL。如果您有一个具有多个 PHP 调用点/脚本的传统 Web 应用程序,这是有意义的。

    请注意,MultiView 有一个不同的/更广泛的用途。它会导致 非常小性能下降,因为 Apache 总是查找具有匹配基名的其他文件。它实际上是为 内容-谈判设计的,因此浏览器在可用资源(如 article.en.phparticle.fr.phparticle.jp.mp4)中得到了最好的选择。

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

    在 URL 中避免使用 .php后缀的一种更直接的方法是 配置 PHP 处理程序用于其他文件模式。最简单的选项是通过 .htaccess覆盖默认的 MIME/处理程序类型:

    DefaultType application/x-httpd-php
    

    这样,您可以只将 article.php脚本重命名为 article(没有扩展) ,但仍然可以将其处理为 PHP 脚本。

    现在这可能涉及到一些安全性和性能问题,因为所有无扩展文件现在都将通过 PHP 传输。因此,您可以选择仅为单个文件设置此行为:

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

    这在一定程度上取决于您的服务器设置和使用的 PHP SAPI。

    再次注意,这样的设置会从一个 .htaccess传播到子文件夹。对于静态资源和上传/目录等,应该始终禁用脚本执行(SetHandler NoneOptions -Execphp_flag engine off等)。

  4. 其他 Apache 重写方案

    在众多选项中,Apache 提供了 mod_alias特性——有时候这些特性和 mod_rewrite的 RewriteRules 一样好。注意,其中大部分必须在 <VirtualHost>部分中设置,而不是在每个目录的 .htaccess配置文件中设置。

    • ScriptAliasMatch 主要用于 CGI 脚本,但也应该适用于 PHP。它像任何 RewriteRule一样允许 regexp。实际上,配置一个全面的前端控制器可能是最健壮的选项。

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

    • 甚至一个简单的 ErrorDocument指令也可以用来让 PHP 脚本处理虚拟路径。但是请注意,这是一个复杂的解决方案,禁止除 GET 请求以外的任何请求,并且根据定义洪水泛滥 error.log。

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

参考文献

Stack Overflow 拥有 很多的其他优秀资源:

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

常用的占位符

  • .*匹配任何东西,即使是空字符串。您不希望在任何地方都使用这种模式,但是通常在最后一个备用规则中使用。
  • [^/]+更常用于路径段。它匹配除了正斜杠以外的任何内容。
  • \d+只匹配数字字符串。
  • \w+匹配字母数字字符。它基本上是 [A-Za-z0-9_]的简写。
  • [\w\-]+为“蛞蝓”式路径段,使用字母、数字、破折号 - 还有 _
  • [\w\-.,]+添加句点和逗号。在 […]字符类中更喜欢转义的 \-破折号。
  • \.表示一个文字句点,否则 […]之外的 .就是任何符号的占位符。

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

重写规则

下面的例子是以 PHP 为中心的,并且更加渐进,更容易适应类似的情况。 它们只是总结,通常链接到更多的变化或详细的问答。

  • 静态映射 < br > /contact,< kbd > /about

    将一些页面名称缩短为内部文件模式是最简单的:

     RewriteRule ^contact$  templ/contact.html
    RewriteRule ^about$    about.php
    
  • Numeric identifiers
    /object/123

    Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
    #                      └───────────────────────────┘
    
  • Slug-style placeholders
    /article/with-some-title-slug

    You can easily extend that rule to allow for /article/title-string placeholders:

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

    请注意,你的剧本 必须的能够(或被改编)将这些标题映射回数据库 ID。仅凭重写规则无法凭空创建或猜测信息。

  • 数字前缀 < br > < kbd > /readable/123-plus-title的蛞蝓

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

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

    现在您可以跳过传递 title=$2,因为您的脚本通常依赖于 database-id。-title-slug已经成为任意 URL 的装饰。

  • 与替代列表的一致性 < br > /foo/… /bar/… < kbd > /baz/…

    如果对于多个虚拟页路径有类似的规则,那么可以使用 |替代列表来匹配和压缩它们。然后再次将它们重新分配到内部 GET 参数:

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

    如果这个过于复杂,您可以将它们分成单独的 RewriteRule

  • 将相关 URL 发送到不同的后端 < br > < kbd > /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 参数。

  • 其他分隔符不仅仅是 /路径斜杠 < br > < kbd > /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
    #                   └─────┼────────────────────┘       │
    #                         └────────────────────────────┘
    

    有时甚至可以在同一规则下在 /-分隔符和 :.之间交替使用。或者再次使用两个重写规则来将变量映射到不同的脚本。

  • 可选的后跟 /斜线 < br > /dir = < kbd > /dir/

    当选择目录样式的路径时,可以使其可访问,无论是否使用最终的 /

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

    现在这个函数同时处理 http://example.com/blog/123/blog/123/。而且 /?$方法很容易附加到任何其他重写规则上。

  • 虚拟路径的柔性段 < br > < kbd > .*/.*/.*/.*

    您将遇到的大多数规则都会将一组受约束的 /…/资源路径段映射到单个 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
    #              └─────┴─────┴───────────────────┴────┴────┘
    

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

    或者你可以利用 PHP 阵列参数通过 ?p[]=$1&p[]=$2&p[]=3查询字符串在这里-如果你的脚本只是喜欢他们预拆分。 (尽管更常见的做法是仅使用一个 catch-all 规则,并让脚本本身展开 REQUEST _ URI 之外的段。)

    另见: < a href = “ https://stackoverflow. com/questions/3655893”> 如何将 URL 路径段转换为查询字符串键值对?

  • 可选段 < br > < kbd > prefix/opt?/.*

    一个常见的变化是使用可选的前缀 内心作为规则。如果您周围有静态字符串或更多约束的占位符,这通常是有意义的:

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

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

  • 捕获其余部分 < br > < kbd > /prefix/123-capture/…/*/…whatever…

    如前所述,您通常不想要太一般的重写模式。然而,有时将静态和特定的比较与 .*结合起来确实是有意义的。

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

    这可以选择任何 /…/…/…跟踪路径段。然后当然需要处理脚本将它们分开,并且 Variabl-ify提取参数 本身(这是 网站“ MVC”框架所做的)

  • 后面的文件“扩展名”< br > < kbd > /old/path.HTML

    URL 实际上没有文件扩展名。这就是整个引用的内容(= 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处理程序,或者仅仅为单个(实际/真实)文件使用别名。

  • 乒乓球(同步重定向和重写) < br > /ugly.html∞→ < kbd > /pretty

    因此,在某些时候,您需要重写 HTML 页面,以便只带有漂亮的链接,比如 由十字形轮廓。 同时,您仍然会收到对 老了路径的请求,有时甚至是来自书签的请求。作为 解决办法,您可以使用乒乓球浏览器来显示/建立 新的网址。

    这个常见的技巧包括,每当传入的 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,重新映射回丑陋的路径,没有无限循环

  • 模式 < br > < kbd > /this+that+中的空格

    在浏览器地址栏中不使用 那么漂亮,但是可以在 URL 中使用空格。对于重写模式,使用反斜杠转义的 \␣空格。 否则只需引用 "-整个模式或替换:

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

    客户端使用 +%20对空格序列化 URL。然而,在 RewriteRules 中,它们被解释为所有相对路径段的文本字符。

常见副本:

  • 中央调度员/前端控制器脚本的全部内容

     RewriteCond %{REQUEST_URI} !-f
    RewriteCond %{REQUEST_URI} !-d
    RewriteRule ^.*$     index.php   [L]
    

    PHP 框架或 WebCMS/门户脚本经常使用它。然后在 PHP 中使用 $_SERVER["REQUEST_URI"]处理实际的路径分割。所以从概念上讲,它与 URL 处理“ per mod _ rewrite”几乎相反。(只需使用 FallBackResource即可。)

  • 从主机名中删除 www.

    请注意,这不会沿着查询字符串进行复制,等等。

     #                               ┌──────────┐
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]  │
    RewriteRule ^(.*)$ http://%1/$1 [R=301,L]  │
    #             ↓           └───┼────────────┘
    #             └───────────────┘
    

    参见:
    · href = “ https://stackoverflow. com/questions/3634101/URL-rewrite-for-different-protocol-in-htaccess”> . htaccess 中不同协议的 URL 重写
    通用的 htaccess 将 www 重定向到 non-www
    如何以一种通用的方式强制“ www”?

    注意,RewriteCond/RewriteRule 组合可能更复杂,匹配(%1$1)甚至在两个方向上都有交互:

    References %1 and $2, %3 between RewriteRule and RewriteCond
    Apache 手册-mod _ rewrite intro ,版权所有2015 Apache软件基金会,AL-2.0

  • 重定向到 HTTPS://

     RewriteCond %{SERVER_PORT} 80
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    

    另见: < a href = “ https://wiki.apache.org/httpd/RewriteHTTPtoHTTPS”rel = “ norefrer”> https://wiki.apache.org/httpd/rewritehttptohttps

  • “删除”PHP 扩展

     RewriteCond %{REQUEST_FILENAME}.php -f
    RewriteRule ^(.+)$  $1.php  [L]  # or [END]
    

    另见: < a href = “ https://stackoverflow. com/questions/4908122/ 移除-the-php 扩展名-with-mod-rewrite”> 用 mod _ rewrite 移除. php 扩展名

  • 别名旧的. html 路径到. php 脚本

    参见: < a href = “ http://httpd.apache.org/docs/2.4/rewrite/reapping.html # backback-腰兼容性”rel = “ norefrer”> http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility

  • 将 URL (如“/page”)重写为脚本(如“/index.php/page”)

    请参阅 < a href = “ https://stackoverflow. com/q/265898”> mod _ rewrite,php 和. htaccess 文件

  • 将子域重定向到一个文件夹

    参见“如何让我的 htaccess 访问工作(子域名)”?

流行的 .htaccess陷阱

现在对此持保留态度。不是所有的建议都可以推广到所有的情况。 这只是一个众所周知的和一些不明显的绊脚石的简单总结:

  • 启用 mod_rewrite.htaccess

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

    • 检查服务器是否有 启动 AllowOverride All,否则每个目录的 .htaccess指令将被忽略,并且 RewriteRules 将无法工作。

    • 显然,httpd.conf模块部分中有 启用 mod_rewrite

    • 在每个规则列表前面仍然使用 RewriteEngine On。虽然 mod _ rewrite 在 <VirtualHost><Directory>部分是隐式活动的, 每个目录的 .htaccess文件需要单独调用它

  • 前斜线 ^/不匹配

    正常情况下,你不应该用 ^/开始你的 .htaccess重写规则模式:

     RewriteRule ^/article/\d+$  …
    ↑
    

    这在以前的教程中经常出现。对于古老的 Apache 1.x 版本来说,它曾经是正确的。现在,在 .htaccess重写规则中,请求路径是方便的 Full < strong > 目录相对 。只要把前导 /放在外面就行了。

    · 请注意,在 <VirtualHost>部分中,前面的斜杠仍然是正确的。这就是为什么你经常看到它 ^/?可选的规则奇偶性。
    或者在使用 RewriteCond %{REQUEST_URI}的时候,你仍然可以匹配领先的 /
    参见 SE: mod _ rewrite 模式中什么时候需要前斜杠(/) ?

  • 收起 <IfModule *>包装纸!

    你可能在很多例子中看到过这一点:

    <IfModule mod_rewrite.c>
    Rewrite…
    </IfModule>
    
    • 是的<VirtualHost>部分中是有意义的——如果它与另一个后备选项(如 ScriptAliasMatch)组合在一起的话。(但是从来没有人这么做过)。
    • 它通常分布在许多开源项目的默认 .htaccess规则集中。在那里,它只是意味着回退,并保持“丑陋”的 URL 作为默认工作。

    但是你的 不想通常在你自己的 .htaccess文件。

    • 首先,mod _ rewrite 不会随机脱离(如果脱离,你会有更大的问题)。
    • 如果它真的被禁用了,您的重写规则仍然无法工作。
    • 这是为了防止 HTTP 500错误。它通常实现的是使用 HTTP404错误为用户增色。(仔细想想,不是 非常爱更加用户友好。)
    • 实际上,它只是抑制更有用的日志条目或服务器通知邮件。为什么你的重写规则从来没有工作的 一无所知

    看似诱人的普遍保障措施,往往成为实践中的障碍。

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

    许多复制 + 粘贴示例包含 RewriteBase /指令。这恰好是隐式默认值。所以你不需要这个。这是一个针对虚拟主机重写方案的变通方案,也是一些共享主机错误的 DOCUMENT _ ROOT 路径。

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

    另请参阅《重写基础如何在.htaccess 中工作》

  • 当虚拟路径重叠时禁用 MultiViews

    URL 重写主要用于支持 虚拟的传入路径。通常您只有一个调度程序脚本(index.php)或几个单独的处理程序(articles.phpblog.phpwiki.php、 ...)。后面的 可能会发生冲突具有类似的虚拟 RewriteRule 路径。

    例如,对 /article/123的请求可以隐式地映射到具有 /123 PATH _ INFO 的 article.php。您可能需要使用常见的 RewriteCond !-f + !-d来保护您的规则,并且/或者禁用 PATH _ INFO 支持,或者可能只是禁用 Options -MultiViews

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

  • 点菜很重要

    关于 mod _ rewrite 你想知道的一切 如果你还没有的话。组合多个 RewriteRules 通常会导致交互。对于 [L]标志来说,这并不是要习惯性地阻止,而是一个你一旦熟练掌握就会接受的方案。 从一个规则到另一个规则的 可以写虚拟路径,直到到达实际的目标处理程序。

    尽管如此,经常仍然希望在 很早规则中拥有最具体的规则(固定字符串 /forum/…模式,或者更具限制性的占位符 [^/.]+)。 一般的 slurp-all 规则(.*)最好留给 回见规则(一个例外是 RewriteCond -f/-d警卫作为主块)

  • 样式表和图像停止工作

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

    • 仅使用服务器绝对引用 href="/old.html"src="/logo.png"
    • 通常只需将 <base href="/index">添加到 HTML <head>部分。 这将隐式地将相对引用重新绑定到它们之前的位置。

    您也可以进一步制作重写规则,将 .css.png路径重新绑定到它们的原始位置。 但是这两者都是不需要的,或者会导致额外的重定向和阻碍缓存。

    另见: CSS、 JS 和图片不显示漂亮的网址

  • RewriteConds 只是屏蔽了一个 RewriteRule

    一个常见的误解是 RewriteCond 阻塞了多个 RewriteRules (因为它们可视化地排列在一起) :

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

    但是默认情况下不是这样的。您可以使用 [S=2]标志 把他们锁起来。否则你就得重复他们的话。虽然有时您可以对[ END ]重写处理提前制定一个“反向”主规则。

  • QUERY _ STRING 不受重写规则约束

    你不能匹配 RewriteRule index.php\?x=y,因为 mod _ rewrite 只能和默认的相对路径进行比较。你可以通过以下方式单独匹配:

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

    参见: 如何将查询字符串变量与 mod _ rewrite 进行匹配?

  • .htaccess<VirtualHost>

    如果您在每个目录的配置文件中使用 RewriteRules,那么担心正则表达式的性能是毫无意义的 编译的 PCRE 模式比使用通用路由框架的 PHP 进程要长 将规则集移动到 vhost 服务器配置中,一旦它们经过实战测试。

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

  • 只要有什么东西坏了

    别担心。

    • 比较 access.logerror.log

      通常,您可以通过查看 error.logaccess.log来了解重写规则是如何不当行为的。 将访问时间关联起来,看看哪个请求路径最初进入,哪个路径/文件 Apache 无法解析(错误404/500)。

      这并不能告诉您哪个重写规则是罪魁祸首。但是像 /docroot/21-.itle?index.php这样无法进入的最终路径可能会暴露出进一步检查的方向。 否则禁用规则,直到得到一些可预测的路径。

    • 启用重写日志

      参见 Apache 重写日志文档。有关调试,您可以在 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'
      

      这有助于缩小过度通用规则和正则表达式错误的范围。

      参见:
      · href = “ https://stackoverflow. com/questions/945047/htaccess-not-working-mod-rewrite”> . htaccess not working (mod _ rewrite)
      · 调试.htaccess 重写规则的提示

    • 在问你自己的问题之前

      正如你可能知道的,Stack Overflow 非常适合提问 mod _ rewrite 的问题,让它们在主题上显示出来 通过包括以前的研究和尝试(避免多余的答案) ,证明基本的 理解,以及:

      • 包括输入 URL 的 满了示例、错误重写的目标路径、真正的目录结构。
      • 完整的 RewriteRule 集,但是 还有挑出了假定有缺陷的那个。
      • Apache 和 PHP 版本、 OS 类型、文件系统、 DOCUMENT _ ROOT 和 PHPs $_SERVER环境(如果与参数不匹配有关)。
      • 摘录自 access.logerror.log,以验证现有规则解析为什么。更好的是,一个 rewrite.log摘要。

      这网更快,更准确的答案,使他们更有用的其他。

  • 评论你的 .htaccess

    如果你从某个地方复制了一些例子,注意要包含一个 # comment and origin link, 它经常真的伤害以后的维护。记录任何代码或教程源代码。特别是当你不熟悉的时候 更有兴趣的是不要把他们当作魔法黑盒子来对待

  • 这不是“搜索引擎优化”-网址

    免责声明: 只是个小毛病。你经常听到漂亮的 URL 重写方案被称为“ SEO”链接或其他东西。虽然这对于谷歌示例很有用,但是这是一个过时的误称。

    没有一个现代的搜索引擎真正被路径段中的 .html.php或者 ?id=123查询字符串所干扰。旧的搜索引擎,如 AltaVista,是的避免爬行网站潜在的模糊访问路径。现代爬虫甚至经常渴望深层网络资源。

    在概念上,“漂亮”的 URL 应该用于制作网站 方便用户使用

    1. 具有可读和明显的资源方案。
    2. 确保 URL 是长寿命的(即 永久链接)。
    3. 通过 /common/tree/nesting提供可发现性。

    然而,不要为了遵从而牺牲独特的需求。

工具

有各种在线工具可以为大多数 GET 参数 URL 生成 RewriteRules:

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

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

我现在有这样的 URL:

我把它们做得这么漂亮:

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

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

但我想让它们看起来像这样:

我如何改变我的.htaccess 文件使之工作?


答案很简单,你做不到。

重写规则不会让难看的 URL 变得漂亮,它们会让漂亮的 URL 变得难看

每当你在浏览器中输入一个 URL,或者点击一个链接,或者显示一个引用图片的页面等等,浏览器就会为一个特定的 URL 生成一个 请求。该请求最终到达一个 Web 服务器,Web 服务器给出一个 回应

改写规则是一个简单的规则,它说“当浏览器 请求的 URL 看起来像 X,给他们相同的 回应,如果他们已经请求 Y”。

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

你不能用你没有的信息

考虑到重写规则的这个基本模型,假设您正在向人类提供指令。你可以说:

  • 如果您在请求中看到一个数字,比如“ 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 号是多少。

但有些网站这样做,所以它必须是可能的!

Web 服务器只有请求中提供的信息,但是 怎么做使用这些信息取决于您。

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

您可以做的另一件事是使用 POST 方法而不是 GET 方法的表单(或 API 请求)。这意味着附加信息在请求的“主体”中发送,与 URL 分开。它仍然必须是 发送,但是在浏览器中不那么明显,不会包含在书签中,等等。

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