首页 文章

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

提问于
浏览
132

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

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

4 回答

  • 72

    要了解什么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解释器,然后返回结果 . 所有这些关联都是完全可配置的;一个文件不必匹配磁盘上的任何特定文件以发生某些事情 .

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

    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并没有神奇地使你的所有网址“漂亮” . 这是一种常见的误解 . 如果您的网站中有此链接:

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

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

    • 将链接更改为漂亮的链接:
    <a href="/my/pretty/link">
    
    • 使用服务器上的mod_rewrite使用上述任何一种方法处理对URL的请求 /my/pretty/link .

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

    非常有'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'如果您了解基本的HTTP请求 - 响应机制,那么功能强大且可以用得很好 . 它不会自动使您的链接漂亮 .

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

  • 36

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

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

    重写示例

    让我们举个例子:

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

    该规则分为4个部分:

    • RewriteRule - 启动重写规则

    • ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - 这被称为模式,但是我只是将它称为规则的左侧 - 你要重写的是什么

    • blog/index.php?id=$1&title=$2 - 调用重写规则的替换或右侧 - 您想要重写的内容

    • [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)或 -+ (注意 + 使用反斜杠进行转义,因为没有转义,这将执行regex repetition character) . 这可以通过规则右侧的 $2 引用

    • ? 表示前面的字符是可选的,所以在这种情况下 /blog/1/foo//blog/1/foo 都会重写到同一个地方

    • $ 表示这是我们想要匹配的字符串的结尾

    旗帜

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

    NC
    

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

    L
    

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

    END
    

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

    QSA
    

    查询字符串append标志允许我们将额外的变量传递给指定的URL,该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]
    

    重写条件

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

    # 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]
    

    这稍微不那么常见,但是如果一个规则没有执行,那么这是一个很好的例子filename是服务器上存在的目录或文件 .

    • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] 将仅对文件扩展名为jpg,jpeg,gif或png(不区分大小写)的文件执行重写 .

    • %{REQUEST_FILENAME} !-f 将检查当前服务器上是否存在该文件,如果不存在则仅执行重写

    • %{REQUEST_FILENAME} !-d 将检查当前服务器上是否存在该文件,如果不存在则仅执行重写

    • 重写将尝试在另一个域上加载相同的文件

  • 5

    参考文献

    Stack Overflow还有许多其他很好的资源可供使用:

    对新人友好的正则表达式概述甚至:

    Oft-used占位符

    • .* 匹配任何内容,甚至是空字符串 . 您不希望在任何地方使用此模式,但通常在最后一个回退规则中使用 .

    • [^/]+ 更常用于路径段 . 除了正斜杠之外,它匹配任何东西 .

    • \d+ 仅匹配数字字符串 .

    • \w+ 匹配字母数字字符 . 它基本上是 [A-Za-z0-9_] 的简写 .

    • [\w\-]+ for "slug"-style路段,使用字母,数字,破折号 -_

    • [\w\-.,]+ 添加句点和逗号 . 首选 […] charclasses中的转义 \- 破折号 .

    • \. 表示文字句号 . 否则 […] 之外的 . 是任何符号的占位符 .

    这些占位符中的每一个通常都包含在 (…) 括号中作为捕获组 . 并且整个模式通常在 ^………$ 开始结束标记 . 引用"patterns"是可选的 .

    RewriteRules

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

    静态映射/联系,/ about

    将一些页面名称缩短为内部文件方案非常简单:

    RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    

    数字标识符/对象/ 123

    向现有PHP脚本引入像 http://example.com/article/531 这样的快捷方式也很容易 . 数字占位符只能重新映射到 $_GET 参数:

    RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    

    Slug风格的占位符/ article / with-some-title-slug

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

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

    请注意,您的脚本 must 能够(或适应)将这些 Headers 映射回数据库ID . 仅凭RewriteRules无法凭空创建或猜测信息 .

    带有数字前缀/可读/ 123-plus-title的S ..

    因此,您经常会看到在实践中使用的混合 /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参数 .

    除了/ path slashes / user-123-name之外的其他分隔符

    你're most commonly seeing RewriteRules to simulate a virtual directory structure. But you'不是被迫没有创造力 . 您也可以使用 - 连字符进行分段或结构化 .

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

    对于common / wiki:section:Page_Name方案:

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

    偶尔,它甚至适用于在同一规则中在 / -delimiters和 :. 之间交替 . 或者再次使用两个RewriteRules将变体映射到不同的脚本 .

    可选尾随/斜杠/目录= /目录/

    选择目录样式路径时,可以使用和不使用final /

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

    现在它处理 http://example.com/blog/123/blog/123/ . 并且/?$方法很容易附加到任何其他RewriteRule上 .

    虚拟路径的灵活段 . * / . * / . * / . *

    您将遇到的大多数规则将约束的 /…/ 资源路径段映射到单个GET参数 . 但有些脚本handle a variable number of options . 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 查询字符串利用PHP数组参数 - 如果你的脚本只是喜欢它们预先拆分 . (虽然使用全能规则更常见,让脚本本身扩展REQUEST_URI中的段 . )

    另见:How do I transform my URL path segments into query string key-value pairs?

    可选段前缀/ opt?/.*

    常见的变体是在规则中具有可选前缀 . 如果你有静态字符串或更多约束占位符,这通常是有意义的:

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

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

    捕获余数/前缀/ 123-capture / ... / * / ......无论如何......

    如前所述,您通常不需要过于通用的重写模式 . 然而,有时将静态和特定比较与 .* 相结合是有意义的 .

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

    这可选任何 /…/…/… 尾随路径段 . 当然,这需要处理脚本将它们拆分,并且可变地提取参数本身(这是Web-"MVC"框架所做的) .

    尾随文件“extensions”/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 处理程序,或仅为单个(实际/实际)文件别名目录名称 .

    乒乓(重定向和重写一致)/ugly.html←→/漂亮

    因此,在某些时候,您正在重写HTML页面以仅携带漂亮的链接,如outlined by deceze . 与此同时,您仍会收到旧路径的请求,有时甚至会收到书签 . 作为解决方法,您可以使用乒乓浏览器来显示/ Build 新URL .

    这个常见技巧涉及在传入的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版本,您可以使用其他解决方法,除了还重新映射查询字符串参数,例如:Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops

    空间␣图案/这个

    它在浏览器地址栏中并不常见,但您可以在URL中使用空格 . 对于重写模式,请使用反斜杠转义 \␣ 空格 . 其他只是 " - 指出整个模式或替代:

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

    客户端使用 +%20 序列化URL以获取空格 . 然而在RewriteRules中,它们用所有相对路径段的文字字符进行解释 .

    经常重复:

    Catch-all用于中央调度程序/前端控制器脚本

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

    这通常由PHP框架或WebCMS /门户脚本使用 . 然后使用 $_SERVER["REQUEST_URI"] 在PHP中处理实际路径拆分 . 所以从概念上来说,这几乎与URL处理相反"per mod_rewrite" . (只需使用FallBackResource . )

    删除www . 来自主机名

    请注意,这不会复制查询字符串等 .

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

    也可以看看:
    ·URL rewriting for different protocols in .htaccess
    ·Generic htaccess redirect www to non-www
    ·.htaccess - how to force "www." in a generic way?

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

    References %1 and $2, %3 between RewriteRule and RewriteCond

    Apache手册 - mod_rewrite简介,版权所有2015 The Apache Software Foundation,AL-2.0

    重定向到HTTPS://

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

    另见:https://wiki.apache.org/httpd/RewriteHTTPToHTTPS

    “删除”PHP扩展

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

    另见:Removing the .php extension with mod_rewrite

    将旧的.html路径别名为.php脚本

    见:http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility

    从“/ page”等URL重写为“/index.php/page”等脚本

    mod_rewrite, php and the .htaccess file

    将子域重定向到文件夹

    How can i get my htaccess to work (subdomains)?

    普遍存在.htaccess陷阱

    现在拿一粒盐 . 并非所有建议都可以推广到所有情境 . 这只是一个众所周知的和一些不明显的绊脚石的简单总结:

    启用mod_rewrite和.htaccess

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

    • 检查您的服务器是否有AllowOverride All enabled . 否则,您的每个目录 .htaccess 指令将被忽略,并且RewriteRules将不起作用 .

    • 显然 httpd.conf 在你的 httpd.conf 模块部分 .

    • 使用 RewriteEngine On 前置每个规则列表 . 虽然mod_rewrite在 <VirtualHost><Directory> 部分中是隐式激活的,但是每个目录 .htaccess 文件需要单独召唤它 .

    前导斜杠^ /将不匹配

    您不应该使用 ^/ 正常启动 .htaccess RewriteRule模式:

    RewriteRule ^/article/\d+$  …
                  ↑
    

    这在旧教程中经常出现 . 它曾经是古代Apache 1.x版本的正确版本 . 现在,请求路径在 .htaccess RewriteRules中方便地完全 directory-relative . 只要离开领先 / .

    ·请注意,在 <VirtualHost> 部分中,前导斜杠仍然正确 . 这就是为什么你经常看到 ^/? 为规则奇偶校验可选 .
    ·或者当使用 RewriteCond %{REQUEST_URI} 时,你仍然匹配领先的 / .
    ·参见Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?

    <IfModule *>包装器开始!

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

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • <VirtualHost> 部分确实有意义 - 如果它与另一个后备选项结合使用,例如ScriptAliasMatch . (但没有人这样做过) .

    • 它通常用于许多开源项目的默认 .htaccess 规则集 . 在那里,它只是作为后备,并保持"ugly" URL作为默认值 .

    但是你 don't want 通常在你自己的 .htaccess 文件中 .

    • 首先,mod_rewrite不会随机脱离 . (如果确实如此,你会遇到更大的问题) .

    • 如果真的被禁用了,你的RewriteRules仍然无法运行 .

    • 这是为了防止HTTP 500 错误 . 它通常会完成的工作是使用HTTP 404 错误来为您的用户提供支持 . (如果你考虑一下,那就不那么友好了 . )

    • 实际上它只是抑制了更有用的日志条目或服务器通知邮件 . 关于为什么你的RewriteRules永远不会工作,你应该 none the wiser .

    看似诱人的广义保障措施,往往在实践中成为障碍 .

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

    许多复制粘贴示例包含 RewriteBase / 指令 . 无论如何,这恰好是隐式默认值 . 因此,您不必使用虚拟主机重写方案的解决方法,并为某些共享主机提供错误的DOCUMENT_ROOT路径 .

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

    另见How does RewriteBase work in .htaccess

    虚拟路径重叠时禁用MultiView

    URL重写主要用于支持虚拟传入路径 . 通常,您只有一个调度程序脚本( index.php )或一些单独的处理程序( articles.phpblog.phpwiki.php ,...) . 后者might clash具有类似的虚拟RewriteRule路径 .

    例如,对 /article/123 的请求可能会隐式地使用 /123 PATH_INFO映射到 article.php . 你要么必须保护你的规则然后使用普通的地方 RewriteCond !-f !-d ,和/或禁用PATH_INFO支持,或者只是禁用 Options -MultiViews .

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

    订购很重要

    如果您没有习惯性地预防 [L] 旗帜,请参阅Everything you ever wanted to know about mod_rewrite,但这是一个精通您的计划 . 您 can re-re-rewrite 从一个规则到另一个规则的虚拟路径,直到它到达实际的目标处理程序 .

    您仍然希望在早期规则中拥有最具体的规则(固定字符串 /forum/… 模式或更严格的占位符 [^/.]+ ) . 通用的slurp-all规则( .* )最好留给以后的规则 . (一个例外是 RewriteCond -f/-d guard作为主要块 . )

    样式表和图像停止工作

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

    • 仅使用服务器绝对引用 href="/old.html"src="/logo.png"

    • 通常只需将 <base href="/index"> 添加到HTML <head> 部分即可 . 这隐含地重新引用了之前的相对引用 .

    您可以选择制作更多RewriteRules以将 .css.png 路径重新绑定到其原始位置 . 但这既不需要,也不会引发额外的重定向和阻碍缓存 .

    另见:CSS, JS and images do not display with pretty url

    RewriteConds只掩盖一个RewriteRule

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

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

    它默认不是 . 您可以chain them使用 [S=2] 标志 . 否则你将不得不重复它们 . 虽然有时您可以制定一个"inverted"主要规则到[END]早期重写处理 .

    QUERY_STRING免于RewriteRules

    您无法匹配 RewriteRule index.php\?x=y ,因为mod_rewrite仅与每个默认值的相对路径进行比较 . 您可以通过以下方式单独匹配它们:

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

    另见How can I match query string variables with mod_rewrite?

    .htaccess与<VirtualHost>

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

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

    每当某些东西不起作用时

    不要烦恼 .

    • Compare access.log and error.log

    通常你可以通过查看 error.logaccess.log 来弄清楚RewriteRule是如何行为不端的 . 关联访问时间以查看最初进入的请求路径以及Apache无法解析的路径/文件(错误404/500) .

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

    • Enable the RewriteLog

    Apache RewriteLog docs . 对于调试,您可以在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 not working (mod_rewrite)
    ·Tips for debugging .htaccess rewrite rules

    • Before asking your own question

    您可能知道,Stack Overflow非常适合在mod_rewrite上提问 . 通过包括先前的研究和尝试(避免多余的答案)使它们成为on-topic,展示基本的理解,并且:

    • 包括输入URL的完整示例,错误重写的目标路径,真实的目录结构 .

    • 完整的RewriteRule设置,但也挑出假定的有缺陷的一个 .

    • Apache和PHP版本,操作系统类型,文件系统,DOCUMENT_ROOT和PHP $_SERVER 环境,如果它与参数不匹配有关 .

    • 摘自 access.logerror.log 以验证现有规则解决的问题 . 更好的是, rewrite.log 摘要 .

    这样可以更快,更准确地回答问题,并使其对其他人更有用 .

    评论您的.htaccess

    如果您从某处复制示例,请注意包含 # comment and origin link . 虽然省略归属只是不礼貌,但以后往往会伤害维护 . 记录任何代码或教程源 . 特别是在没有逆转的情况下,你应该更感兴趣的是不要像魔术黑盒一样对待它们 .

    这不是“SEO”-URLs

    免责声明:只是一个小小的烦恼 . 您经常听到漂亮的URL重写方案,称为"SEO"链接或其他东西 . 虽然这对谷歌搜索示例很有用,但这是一个过时的用词不当 .

    没有一个现代搜索引擎真的被路径段中的 .html.php 干扰,或者 ?id=123 查询这个问题的字符串 . 旧的搜索引擎,例如AltaVista,确实避免了抓取具有潜在暧昧访问路径的网站 . 现代爬虫通常甚至渴望获得深层网络资源 .

    什么"pretty" URL应该在概念上用于制作网站 user-friendly .

    • 具有可读和明显的资源方案 .

    • 确保URL是长期存在的(AKA permalinks) .

    • 通过 /common/tree/nesting 提供可发现性 .

    但是,不要牺牲符合性的独特要求 .

    工具

    有大量的在线工具可以为大多数GET参数化URL生成RewriteRules:

    通常只输出 [^/]+ 通用占位符,但可能足以满足琐碎的网站 .

  • 96

    mod_rewrite的替代品

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

    使用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那样方便 . 它只是用较少的配置工作来模拟"pretty URLs" .

    启用MultiViews以隐藏.php扩展名

    在URL中避开 .php "file extensions"的最简单选项是:

    Options +MultiViews
    

    由于匹配的基本名称,这使得Apache在 /article 上的HTTP请求选择 article.php . 这与上述PATH_INFO功能一起使用效果很好 . 所以你可以使用像 http://example.com/article/virtual/title 这样的网址 . 如果您有一个带有多个PHP调用点/脚本的传统Web应用程序,这是有意义的 .

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

    用于无扩展名.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 None和Options -Exec或php_flag引擎关闭等)以获取静态资源,上传/目录等 .

    其他Apache重写方案

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

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

    • 简单的Alias也有一些简单的重写方案 .

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

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

相关问题