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

时间:2021-09-08 11:19:39

"Pretty links" is an often requested topic, but it is rarely fully explained. mod_rewrite is one way to make "pretty links", but it's complex and its syntax is very terse, hard to grok and the documentation assumes a certain level of proficiency in HTTP. Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?

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

Other common names, aliases, terms for clean urls: RESTful URLs, User-friendly URLs, SEO-friendly URLs, Slugging, MVC urls (probably a misnomer)

其他常用的名称、别名、干净url的术语:RESTful url、用户友好的url、友好的url、Slugging、MVC url(可能是用词不当)

4 个解决方案

#1


87  

To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:

要理解什么是mod_rewrite,您首先需要了解web服务器是如何工作的。web服务器响应HTTP请求。HTTP请求的最基本级别是这样的:

GET /foo/bar.html HTTP/1.1

This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:

这是浏览器对请求URL /foo/bar的web服务器的简单请求。html。重要的是强调它不请求文件,它只请求一些任意的URL。请求也可能是这样的:

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

This is just as valid a request for a URL, and it has more obviously nothing to do with files.

这对于URL的请求是有效的,而且它与文件没有任何关系。

The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)

web服务器是在端口上侦听的应用程序,接收来自该端口的HTTP请求并返回响应。web服务器可以完全*地以任何方式响应任何请求,而您已经将其配置为响应。这个响应不是一个文件,它是一个HTTP响应,它可能与任何磁盘上的物理文件无关。web服务器不必是Apache,还有许多其他的web服务器,它们都是持久运行的程序,并连接到一个响应HTTP请求的端口。你可以自己写。这一段的目的是要让您从任何一个url直接等于文件的概念中分离出来,这一点非常重要。:)

The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.

大多数web服务器的默认配置是查找与硬盘上的URL相匹配的文件。如果将服务器的文档根设置为/var/www,则可以查看文件/var/ www/foo/bar。html存在并为它服务。如果文件结束。php“将调用php解释器,然后返回结果。所有这些关联都是完全可配置的;一个文件不需要结束。php“让web服务器通过php解释器运行它,而URL不需要匹配磁盘上的任何特定文件以实现某些事情。

mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:

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

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:

该规则表示,每当请求匹配“/foo/bar”时,将其重写为“/foo/baz”。然后,请求将被处理,就像请求/foo/baz一样。这可以用于各种效果,例如:

RewriteRule (.*) $1.html

This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.

这个规则匹配任何(.*)并捕获它(..),然后重写它以附加“.html”。换句话说,如果/foo/bar是请求的URL,那么它将被处理成if /foo/bar。html被请求。有关正则表达式匹配、捕获和替换的更多信息,请参见http://regular-expressions.info。

Another often encountered rule is this:

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

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

This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.

这再次匹配任何内容并将其改写为文件索引。php与原始请求的URL附加在URL查询参数中。即。,对于所有传入的请求,文件索引。php被执行,这个文件将访问$_GET['url']中的原始请求,因此它可以做任何它想做的事情。

What mod_rewrite does not do

mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:

mod_rewrite并不能神奇地使你的url“漂亮”。这是一个常见的误解。如果你的网站有这个链接:

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

there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:

没有任何mod_rewrite可以做得这么漂亮。为了使这个链接更漂亮,你必须:

  1. Change the link to a pretty link:

    更改链接到一个漂亮的链接:

    <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.

    使用上面描述的任何方法,在服务器上使用mod_rewrite来处理对URL /my/pretty/link的请求。

(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.)

(我们可以使用mod_replace来转换传出的HTML页面及其包含的链接。尽管这比仅仅更新HTML资源要花费更多的精力。

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.

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

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

查看所有可能的标志和选项的官方文档。

#2


66  

To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.

为了扩展“欺骗”的答案,我想提供一些例子和一些其他mod_rewrite功能的解释。

All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.

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

Rewrite Example

Lets take this example:

让我们用这个例子:

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

The rule is split into 4 sections:

该规则分为4个部分:

  1. RewriteRule - starts the rewrite rule
  2. 重写—开始重写规则。
  3. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from
  4. ^博客/([0 - 9]+)/([A-Za-z0-9 - \ +]+)/ ?$ -这称为模式,不过我将把它称为规则的左手边—你想要重写的东西。
  5. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to
  6. 博客/ index . php ?id=$1&title=$2 -被称为替换,或重写规则的右端-您想要重写的内容。
  7. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later
  8. [NC,L,QSA]是重写规则的标志,用逗号分隔,稍后我将解释更多。

The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.

上面的重写会让你链接到像/blog/1/foo/这样的东西,它实际上会加载/blog/index.php?id=1&title=foo。

Left hand side of the rule

  • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...
  • ^表示开始页面的名称,所以它将改写example.com/blog/..。但不是example.com/foo/blog/..。
  • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:
    • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule
    • 第一组括号——([0-9]+)——匹配一个长度最小为1字符的字符串,并且只匹配数字值(即0-9)。这可以在规则右边的$1中引用。
    • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule
    • 第二组括号匹配长度最小为1字符的字符串,只包含字母数字字符(a-z、a-z或0-9)或-或+(注释+被反斜杠转义,因为没有转义,这将作为regex重复字符执行)。这可以在规则右边的$2中引用。
  • 每一组(…)括号表示一个正则表达式,我们可以将其捕获为规则右边的变量。在这个例子中:第一组括号-([0-9]+)-匹配一个长度最小为1字符的字符串,并且只匹配数字值(即0-9)。这可以引用1美元在规则的右手边第二组括号匹配字符串的最低1字符长度,只包含字母数字字符(a - z、a - z或0 - 9)或-或+(注意+用反斜杠转义为没有逃离这将作为一个正则表达式执行重复字符)。这可以在规则右边的$2中引用。
  • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place
  • 吗?意味着前面的字符是可选的,因此在本例中,两个/blog/1/foo/和/blog/1/foo将重写到相同的位置。
  • $ indicates this is the end of the string we want to match
  • $表示这是我们要匹配的字符串的结尾。

Flags

These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:

这些选项是在重写规则末尾的方括号中添加的,以指定特定的条件。同样,有很多不同的标志你可以在文档中看到,但是我要讲一些比较常见的标志:

NC

The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.

没有case标志意味着重写规则是不区分大小写的,因此对于上面的示例规则,这意味着两个/blog/1/foo/和/blog/1/foo/(或任何其他的变体)都将被匹配。

L

The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.

最后一个标志表示这是应该处理的最后一条规则。这意味着,如果且仅当此规则匹配时,在当前重写处理运行中不会对其他规则进行评估。如果规则不匹配,所有其他规则将按照惯例进行尝试。如果您没有设置L标志,那么所有下面的规则将被应用到重写后的URL。

END

Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)

因为Apache 2.4也可以使用[END]标志。匹配的规则将完全终止别名/重写处理。(然而,[L]标志通常可以触发第二轮,例如,当重写或退出子目录时)。

QSA

The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15

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

R

This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:

这个标志不是我在上面的例子中使用的,但是我认为它值得一提。这允许您指定一个http重定向,其中包含一个状态代码(例如,R=301)。例如,如果你想在/myblog/ to /blog上做一个301重定向,你可以简单地写一条这样的规则:

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

Rewrite Conditions

Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:

重写条件使重写更加强大,允许您为更具体的情况指定重写。在文档中有很多可以阅读的条件,但是我将介绍一些常见的例子并解释它们:

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

This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/

这是一种非常常见的做法,它将以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]

This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.

这有点不太常见,但是如果文件名是服务器上存在的目录或文件,那么它就是一个不执行规则的好例子。

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).
  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]只会在文件扩展为jpg、jpeg、gif或png(大小写不敏感)时执行重写。
  • %{REQUEST_FILENAME} !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't
  • %{REQUEST_FILENAME} !-f将检查该文件是否存在于当前服务器上,如果它不存在,则只执行重写。
  • %{REQUEST_FILENAME} !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't
  • %{REQUEST_FILENAME} !-d将检查该文件是否存在于当前服务器上,如果它不存在,则只执行重写。
  • The rewrite will attempt to load the same file on another domain
  • 重写将尝试在另一个域中加载相同的文件。

#3


35  

References

Stack Overflow has many other great resources to get started:

Stack Overflow还有很多其他的资源可以启动:

And newcomer-friendly regex overviews even:

而纽科姆友好的regex overviews甚至:

  • Our tag wiki for a syntax compendium.
  • 我们的regex标记wiki用于语法概要。
  • And the short Apache regex summary.
  • 以及简短的Apache regex概要。
  • Else regexp.info for easy-to-understand basics.
  • 其他的regexp.info用于容易理解的基础。

Oft-used placeholders

  • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.
  • .*匹配任何东西,即使是空字符串。您不希望在任何地方使用这种模式,但通常在最后的回退规则中使用。
  • [^/]+ is more often used for path segments. It matches anything but the forward slash.
  • [^ /]+经常用于路径段。它与前面的斜杠匹配。
  • \d+ only matches numeric strings.
  • \d+只匹配数字字符串。
  • \w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].
  • \ w +字母数字字符匹配。它基本上是[A-Za-z0-9_]的缩写。
  • [\w\-]+ for "slug"-style path segments, using letters, numbers, dash - and _
  • 用字母、数字、破折号和_来表示“slug”式的路径段。
  • [\w\-.,]+ adds periods and commas. Prefer an escaped \- dash in […] charclasses.
  • (\ w \ -。+添加句号和逗号。在[…]charclasses中更倾向于逃离的\- dash。
  • \. denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.
  • \。表示一段文字。否则。在[…]之外是任何符号的占位符。

Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.

每个占位符通常用(…)括号括起来作为捕获组。而整个模式通常是在…$ start + end标记。引用“模式”是可选的。

RewriteRules

The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases. They're just summaries, often link to more variations or detailed Q&As.

下面的示例是php为中心的,并且稍微增加一点,以适应类似的情况。他们只是总结,经常链接到更多的变化或详细的Q&As。

  • Static mapping
    /contact, /about

    Shortening a few page names to internal file schemes is most simple:

    将几个页面名称缩短为内部文件方案最简单:

     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:

    在现有的PHP脚本中引入诸如http://example.com/article/531这样的快捷方式也很简单。数字占位符可以重新映射到$_GET参数:

     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
     #                       └────────────────────────────────┘
    

    Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.

    注意,您的脚本必须能够(或被修改)将这些标题映射回数据库id。一个人不能凭空创造或猜测信息。

  • Slugs with numeric prefixes
    /readable/123-plus-title

    Therefore you'll often see mixed /article/529-title-slug paths used in practice:

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

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

    Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.

    现在您可以跳过标题=$2,因为您的脚本通常会依赖于数据库id。- titleslug已经成为任意的URL装饰。

  • Uniformity with alternative lists
    /foo/… /bar/… /baz/…

    If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:

    如果对多个虚拟页面路径有相似的规则,那么可以使用|替代列表对它们进行匹配和压缩。再把它们重新分配到内部得到参数

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

    You can split them out into individual RewriteRules should this get too complex.

    你可以把它们分成个别的重写,这样就太复杂了。

  • Dispatching related URLs to different backends
    /date/SWITCH/backend

    A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:

    另一种更实际的用法是映射请求路径到不同的脚本。例如,为较老的和基于日期的新web应用程序提供统一的url:

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

    This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler. Note the more specific rule coming first. Each script might use different GET params.

    这简单地将2009-2011年的文章重新映射到一个脚本上,而所有其他年份都隐式地映射到另一个处理程序。请注意,更具体的规则首先出现。每个脚本可能使用不同的GET参数。

  • Other delimiters than just / path slashes
    /user-123-name

    You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.

    您通常会看到RewriteRules模拟虚拟目录结构。但你并不是*缺乏创造力。你也可以用连字符来分割或构造。

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

    For the also common /wiki:section:Page_Name scheme:

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

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

    Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.

    偶尔也适用于/-分隔符和:或。同样的规则。或者有两个RewriteRules重新映射到不同的脚本。

  • Optional trailing / slash
    /dir = /dir/

    When opting for directory-style paths, you can make it reachable with and without a final /

    当选择directory风格的路径时,您可以使它可以在没有final /的情况下实现。

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

    Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.

    现在,它可以同时处理http://example.com/blog/123和/blog/123/。/ ?$方法很容易附加到任何其他的RewriteRule。

  • Flexible segments for virtual paths
    .*/.*/.*/.*

    Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however. The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:

    您将会遇到的大多数规则都映射到单个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
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each. Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.

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

    Alternatively you can utilize PHPs array parameters via ?p[]=$1&p[]=$2&p[]=3 query string here - if your script merely prefers them pre-split. (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)

    或者,您可以在这里使用PHPs数组参数?p[]=$1&p[]=$2&p[]=3查询字符串—如果您的脚本只是喜欢它们预先分割。(不过,使用catch-all规则更常见,并让脚本本身将段扩展到REQUEST_URI。)

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

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

  • Optional segments
    prefix/opt?/.*

    A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:

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

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

    Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.

    现在更复杂的模式(?:/([/])+)?简单地封装一个非捕获(?:…)组,并使其可选)?包含占位符([^ /]+)将替换模式2美元,但是是空的如果没有中间/…/路径。

  • Capture the remainder
    /prefix/123-capture/…/*/…whatever…

    As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.

    如前所述,您通常不需要太通用的重写模式。但是,将静态和特定的比较与a进行比较是有意义的。

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

    This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters itself (which is what Web-"MVC" frameworks do).

    这个选择了任何/…/…当然,这需要处理脚本将它们分割开来,并且变量-ify提取的参数本身(这就是Web-“MVC”框架所做的)。

  • Trailing file "extensions"
    /old/path.HTML

    URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image). However if you had a 1:1 file mapping before, you can craft simpler rules:

    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
    

    Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.

    其他常见的用途是将过时的.html路径重新映射到更新的.php处理程序,或者仅为单个(实际的/真实的)文件混叠目录名。

  • Ping-Pong (redirects and rewrites in unison)
    /ugly.html ←→ /pretty

    So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze. Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish the new URLs.

    因此,在某些时候,你正在重写你的HTML页面,只带一些漂亮的链接,如被欺骗。同时,你仍然会收到旧路径的请求,有时甚至是来自书签。作为变通方法,您可以使用ping-pong浏览器来显示/建立新的url。

    This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme. Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.

    这个常见的技巧是,每当传入的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]
    

    Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping query string parameters for example: Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops

    注意这个示例如何使用[END]而不是[L]安全地交替使用。对于较老的Apache 2.2版本,您可以使用其他的变通方法,而且还可以重新映射查询字符串参数:重定向到漂亮的URL,重新映射到丑陋的路径,没有无限循环。

  • Spaces in patterns
    /this+that+

    It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped \␣ spaces. Else just "-quote the whole pattern or substitution:

    在浏览器地址栏中并不是那么漂亮,但是你可以在url中使用空格。改写模式使用backslash-escaped \␣空间。其他只是"-引用整个模式或替换:

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

    Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.

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

Frequent duplicates:

频繁的重复:

  • Catch-all for a central dispatcher / front-controller script

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

    Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)

    它经常被PHP框架或WebCMS /门户脚本使用。然后,使用$_SERVER[“REQUEST_URI”]在PHP中处理实际的路径拆分。所以从概念上讲,这和URL处理“每个mod_rewrite”是完全相反的。(仅使用FallBackResource代替。)

  • Remove www. from hostname

    Note that this doesn't copy a query string along, etc.

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

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

    See also:
    · URL rewriting for different protocols in .htaccess
    · Generic htaccess redirect www to non-www
    · .htaccess - how to force "www." in a generic way?

    参见:·在.htaccess中对不同协议的URL重写·通用htaccess将www重定向到非www·htaccess——如何以通用的方式强制“www”?

    Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:

    注意,RewriteCond/RewriteRule combos可以更复杂,匹配(%1和$1)在两个方向上相互作用:

    参考:mod_rewrite, URL重写和“漂亮链接”解释。
    Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0

    Apache manual - mod_rewrite intro,版权2015 Apache Software Foundation, AL-2.0。

  • Redirect to HTTPS://

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

    See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS

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

  • "Removing" the PHP extension

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

    See also: Removing the .php extension with mod_rewrite

    请参见:通过mod_rewrite删除.php扩展。

  • Aliasing old .html paths to .php scripts

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

    见:http://httpd.apache.org/docs/2.4/rewrite/remapping.html向后兼容

  • Rewrite from URL like "/page" to a script such as "/index.php/page"

    See mod_rewrite, php and the .htaccess file

    请参阅mod_rewrite、php和.htaccess文件。

  • Redirect subdomain to a folder

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

    看看我如何能让我的htaccess工作(子域名)?

Prevalent .htaccess pitfalls

Now take this with a grain of salt. Not every advise can be generalized to all contexts. This is just a simple summary of well-known and a few unobvious stumbling blocks:

现在把这个带点盐。并不是所有的建议都可以推广到所有的场合。这只是对众所周知的和一些不明显的绊脚石的简单总结:

  • Enable mod_rewrite and .htaccess

    To actually use RewriteRules in per-directory configuration files you must:

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

    • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.

      检查您的服务器是否已经启用了所有启用的权限。否则,您的每个目录.htaccess指令将被忽略,而RewriteRules将不起作用。

    • Obviously have mod_rewrite enabled in your httpd.conf modules section.

      显然,在httpd中启用了mod_rewrite。配置模块部分。

    • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections, the per-directory .htaccess files need it individually summoned.

      使用RewriteEngine对每个规则的列表进行预处理。虽然mod_rewrite在 和 <目录> 段中隐式活动,但是每个目录.htaccess文件需要单独调用。

  • The leading slash ^/ won't match

    You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:

    你不应该开始你的。htaccess RewriteRule模式^ /通常:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.

    这在旧的教程中经常看到。它过去对古代Apache 1是正确的。x版本。现在,请求路径在.htaccess RewriteRules中是很方便的完全直接相关的。请离开前面/出去。

    · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.
    · Or when using a RewriteCond %{REQUEST_URI} you'd still match for a leading /.
    · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?

    注意,在 部分中,主斜杠仍然是正确的。这就是为什么你经常看到它^ / ?optionalized规则平价。·或者当使用RewriteCond %{REQUEST_URI}时,您仍然可以匹配一个引导/。参见网站管理员。在mod_rewrite模式中,什么时候是最需要的斜杠(/)?

  • <IfModule *> wrappers begone!

    You've probably seen this in many examples:

    你们可能在很多例子中见过这个例子:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).
    • 部分——如果它与另一个后备选项(如ScriptAliasMatch)组合在一起,它确实是有意义的。(但从来没有人这样做过)。
    • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.
    • 它通常用于默认的.htaccess规则集,其中包含许多开源项目。它的意思是作为回退,并保持“丑陋”的url作为默认。

    However you don't want that usually in your own .htaccess files.

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

    • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).
    • 首先,mod_rewrite不会随机地脱离。(如果是的话,你会有更大的麻烦)。
    • Were it really be disabled, your RewriteRules still wouldn't work anyway.
    • 如果它真的被禁用了,你的重写程序仍然不管用。
    • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)
    • 它的目的是防止HTTP 500错误。它通常实现的是用HTTP 404错误来优雅用户。(如果你仔细想想的话,就不是那么友好了。)
    • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.
    • 实际上,它只是抑制了更有用的日志条目,或者服务器通知邮件。你不知道为什么你的简历没有成功。

    What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.

    作为一种普遍的保障,看似诱人的东西,往往成为实践中的障碍。

  • Don't use RewriteBase unless needed

    Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.

    许多复制+粘贴示例包含一个RewriteBase /指令。这是默认的默认值。所以实际上你并不需要这个。它是一种用于高级虚拟主机重写方案的变通方案,以及一些共享的主机的DOCUMENT_ROOT路径。

    It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.

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

    See also How does RewriteBase work in .htaccess

    请参见如何在.htaccess中进行RewriteBase工作。

  • Disable MultiViews when virtual paths overlap

    URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might * with similar virtual RewriteRule paths.

    URL重写主要用于支持虚拟传入路径。通常,您只有一个dispatcher脚本(index.php)或一些单独的处理程序(文章)。php,博客。php,wiki。php,……)。后者可能会与类似的虚拟重写路径发生冲突。

    A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.

    例如,对/article/123的请求可以映射到文章。php带有/123 PATH_INFO隐式。你要么必须守住规则,然后再用普通的重写econd !-f+!-d和/或禁用PATH_INFO支持,或者只是禁用选项-多视图。

    Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.

    这并不是说你总是不得不这样做。内容协商只是虚拟资源的一种自动化。

  • Ordering is important

    See Everything you ever wanted to know about mod_rewrite if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed. You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.

    如果您还没有了解mod_rewrite,请查看您想要了解的所有内容。合并多个RewriteRules通常会导致交互。这并不是一种可以习惯性地阻止(L)标志的东西,而是一种你曾经熟悉的计划。您可以从一个规则重新重写虚拟路径到另一个规则,直到它到达一个实际的目标处理程序。

    Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules. Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)

    在早期的规则中,您常常希望拥有最具体的规则(固定的字符串/论坛/…模式,或者更严格的占位符[/.]+)。一般的“啜吸”规则(*)最好留给后面的规则。(一个例外是RewriteCond -f/-d guard作为主块。)

  • Stylesheets and images stop working

    When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>). Which can be solved by:

    当您引入虚拟目录结构/blog/article/123时,这会影响到HTML中的相对资源引用(例如参考:mod_rewrite, URL重写和“漂亮链接”解释。)。可以通过以下方式解决:

    • Only using server-absolute references href="/old.html" or src="/logo.png"
    • 仅使用server-absolute引用href="/old。html”或src = " / logo.png”
    • Often simply by adding <base href="/index"> into your HTML <head> section. This implicitly rebinds relative references to what they were before.
    • 通常只需将添加到HTML 部分。这隐含地将相对引用重新绑定到它们之前的内容。

    You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations. But that's both unneeded, or incurs extra redirects and hampers caching.

    您可以选择进一步修改RewriteRules以重新绑定。css或.png路径到它们的原始位置。但这是不需要的,或者是额外的重定向和妨碍缓存。

    See also: CSS, JS and images do not display with pretty url

    还可以看到:CSS、JS和图像不显示漂亮的url。

  • RewriteConds just mask one RewriteRule

    A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):

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

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

    Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.

    它不是默认的。您可以使用[S=2]标记对它们进行链锁。否则你得重复一遍。虽然有时您可以编写一个“倒转”的基本规则来(结束)早期的重写处理。

  • QUERY_STRING exempt from RewriteRules

    You can't match RewriteRule index.php\?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:

    你不能匹配RewriteRule index。php\?x=y,因为mod_rewrite相对于每个默认路径的相对路径。您可以通过以下方式分别匹配它们:

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

    See also How can I match query string variables with mod_rewrite?

    还可以看到如何使用mod_rewrite匹配查询字符串变量?

  • .htaccess vs. <VirtualHost>

    If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider moving rulesets into the vhost server configuration, once they've been battle-tested.

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

    In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server config files.

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

  • Whenever something doesn't work

    Fret not.

    烦恼。

    • Compare access.log and error.log

      比较访问。日志和error.log

      Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log. Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).

      通常情况下,你可以通过观察你的错误来判断一个RewriteRule的错误行为。日志和access.log。关联访问时间,以查看最初传入的请求路径,以及Apache无法解析的路径/文件(error 404/500)。

      This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further. Otherwise disable rules until you get some predictable paths.

      这并不能告诉你谁是罪犯。但是不可访问的最终路径如/docroot/21-.itle?index。php可能会给出进一步检查的地方。否则禁用规则,直到您获得一些可预测的路径。

    • Enable the RewriteLog

      使RewriteLog

      See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:

      看到Apache RewriteLog文档。对于调试,您可以在vhost部分启用它:

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

      That yields a detailed summary of how incoming request paths get modified by each rule:

      这就得到了每个规则如何修改传入请求路径的详细摘要:

      [..] 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'
      

      Which helps to narrow down overly generic rules and regex mishaps.

      这有助于缩小过于通用的规则和regex事故。

      See also:
      · .htaccess not working (mod_rewrite)
      · Tips for debugging .htaccess rewrite rules

      参见:·.htaccess不工作(mod_rewrite)·调试.htaccess重写规则的提示。

    • Before asking your own question

      在问你自己的问题之前。

      As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic by including prior research and attempts (avoid redundant answers), demonstrate basic understanding, and:

      您可能知道,Stack Overflow非常适合在mod_rewrite上询问问题。通过之前的研究和尝试(避免冗余的答案)使他们成为主题,展示基本的正则表达式理解,并且:

      • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.
      • 包括输入url的完整示例、错误重写的目标路径、真实的目录结构。
      • The complete RewriteRule set, but also single out the presumed defective one.
      • 完整的重写集,但也排除了假定有缺陷的一组。
      • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.
      • 如果是关于参数不匹配的,Apache和PHP版本、OS类型、文件系统、DOCUMENT_ROOT和PHPs $_SERVER环境。
      • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.
      • 摘录自你的访问。日志和错误。日志,以验证现有规则所决定的内容。更好的是,重写。日志总结。

      This nets quicker and more exact answers, and makes them more useful to others.

      这些网更快更准确的回答,使它们对别人更有用。

  • Comment your .htaccess

    If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution, it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be all the more interested in not treating them like magic blackboxes.

    如果您从某个地方复制示例,请注意包含一个#注释和原点链接。虽然省略属性仅仅是不礼貌的行为,但它往往会在以后伤害到维护。编写任何代码或教程源代码。尤其是当你不懂的时候,你应该更感兴趣的是不要把它们当作魔术的黑盒子。

  • It's not "SEO"-URLs

    Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.

    免责声明:只是一个宠物的问题。你经常听到漂亮的URL重写计划被称为“SEO”链接或其他东西。虽然这对于谷歌的例子来说很有用,但它是一种过时的用词不当。

    None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.

    在路径段中,没有任何一个现代搜索引擎真的被.html和.php打扰,或者?id=123查询字符串。旧的搜索引擎,如AltaVista,确实避免了爬行类网站的访问路径。现代的爬虫甚至常常渴望得到深层的网络资源。

    What "pretty" URLs should conceptionally be used for is making websites user-friendly.

    从概念上说,“漂亮”的url应该用来使网站变得友好。

    1. Having readable and obvious resource schemes.
    2. 具有可读和明显的资源计划。
    3. Ensuring URLs are long-lived (AKA permalinks).
    4. 确保url是长寿的(也就是permalinks)。
    5. Providing discoverability through /common/tree/nesting.
    6. 通过/共同/树/提供的可发现性嵌套。

    However don't sacrifice unique requirements for conformism.

    然而,不要为了因循守旧而牺牲独特的要求。

Tools

There are various online tools to generate RewriteRules for most GET-parameterish URLs:

有各种各样的在线工具来为大多数GET-parameterish url生成RewriteRules:

Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.

主要是输出[/]+一般的占位符,但可能满足琐碎的站点。

#4


5  

Alternatives to mod_rewrite

Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.

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

  1. Use the PATH_INFO, Luke

    Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:

    现在,在默认情况下,通常启用了AcceptPathInfo。它基本上允许。php和其他资源url进行虚拟参数:

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

    Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.

    现在,这个/虚拟/路径在PHP中显示为$_SERVER["PATH_INFO"],在这里您可以处理任何额外的参数。

    This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.

    这并不像将Apache单独的输入路径段分割为$1、$2、$3并将它们作为不同的$_GET变量传递给PHP那样方便。它只是模拟“漂亮的url”,而配置的工作量较少。

  2. Enable MultiViews to hide the .php extension

    The simplest option to also eschew .php "file extensions" in URLs is enabling:

    在url中,最简单的选项也可以避免。php“文件扩展”:

    Options +MultiViews
    

    This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.

    这有Apache select文章。php由于匹配的basename而在/文章上请求HTTP请求。这与前面提到的PATH_INFO特性很好地结合在一起。所以你可以使用http://example.com/article/virtual/title。如果您有一个具有多个PHP调用点/脚本的传统web应用程序,这是有意义的。

    Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).

    请注意,多视图具有不同的/更广泛的用途。它会带来非常小的性能损失,因为Apache总是会寻找其他具有匹配basenames的文件。它实际上是用于内容协商的,因此浏览器在可用资源(如article.en)中获得最佳替代。php,article.fr.php article.jp.mp4)。

  3. SetType or SetHandler for extensionless .php scripts

    A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:

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

    DefaultType application/x-httpd-php
    

    This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.

    这样您就可以重命名您的文章了。php脚本(不需要扩展),但仍将其作为php脚本处理。

    Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:

    现在,这可能会带来一些安全性和性能方面的影响,因为所有的extensionless文件都将通过PHP进行管道传输。因此,您可以选择将此行为设置为单独的文件:

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

    This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.

    这有点依赖于您的服务器设置和使用的PHP SAPI。常见的替代方案包括ForceType应用程序/x-httpd-php或AddHandler php5-script。

    Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.

    再次注意,这些设置从一个.htaccess传播到子文件夹。您总是应该禁用脚本执行(SetHandler None和Options -Exec或php_flag引擎等)用于静态资源、上传/目录等。

  4. Other Apache rewriting schemes

    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.

    在它的众多选项中,Apache提供了mod_alias特性,它有时和mod_rewrite RewriteRules一样有效。注意,大多数必须在 部分中设置,而不是在每个目录.htaccess配置文件中。

    • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.

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

    • And a plain Alias helps with a few simple rewriting schemes as well.

      简单的别名也可以帮助编写一些简单的重写方案。

    • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.

      即使是一个简单的ErrorDocument指令也可以用来让一个PHP脚本处理虚拟路径。请注意,这是一个笨拙的工作,但是,它禁止任何东西,但是可以得到请求,并将错误淹没。日志的定义。

    See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.

    请参阅http://httpd.apache.org/docs/2.2/urlmapp.html获取更多提示。

#1


87  

To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:

要理解什么是mod_rewrite,您首先需要了解web服务器是如何工作的。web服务器响应HTTP请求。HTTP请求的最基本级别是这样的:

GET /foo/bar.html HTTP/1.1

This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:

这是浏览器对请求URL /foo/bar的web服务器的简单请求。html。重要的是强调它不请求文件,它只请求一些任意的URL。请求也可能是这样的:

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

This is just as valid a request for a URL, and it has more obviously nothing to do with files.

这对于URL的请求是有效的,而且它与文件没有任何关系。

The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)

web服务器是在端口上侦听的应用程序,接收来自该端口的HTTP请求并返回响应。web服务器可以完全*地以任何方式响应任何请求,而您已经将其配置为响应。这个响应不是一个文件,它是一个HTTP响应,它可能与任何磁盘上的物理文件无关。web服务器不必是Apache,还有许多其他的web服务器,它们都是持久运行的程序,并连接到一个响应HTTP请求的端口。你可以自己写。这一段的目的是要让您从任何一个url直接等于文件的概念中分离出来,这一点非常重要。:)

The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.

大多数web服务器的默认配置是查找与硬盘上的URL相匹配的文件。如果将服务器的文档根设置为/var/www,则可以查看文件/var/ www/foo/bar。html存在并为它服务。如果文件结束。php“将调用php解释器,然后返回结果。所有这些关联都是完全可配置的;一个文件不需要结束。php“让web服务器通过php解释器运行它,而URL不需要匹配磁盘上的任何特定文件以实现某些事情。

mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:

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

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:

该规则表示,每当请求匹配“/foo/bar”时,将其重写为“/foo/baz”。然后,请求将被处理,就像请求/foo/baz一样。这可以用于各种效果,例如:

RewriteRule (.*) $1.html

This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.

这个规则匹配任何(.*)并捕获它(..),然后重写它以附加“.html”。换句话说,如果/foo/bar是请求的URL,那么它将被处理成if /foo/bar。html被请求。有关正则表达式匹配、捕获和替换的更多信息,请参见http://regular-expressions.info。

Another often encountered rule is this:

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

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

This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.

这再次匹配任何内容并将其改写为文件索引。php与原始请求的URL附加在URL查询参数中。即。,对于所有传入的请求,文件索引。php被执行,这个文件将访问$_GET['url']中的原始请求,因此它可以做任何它想做的事情。

What mod_rewrite does not do

mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:

mod_rewrite并不能神奇地使你的url“漂亮”。这是一个常见的误解。如果你的网站有这个链接:

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

there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:

没有任何mod_rewrite可以做得这么漂亮。为了使这个链接更漂亮,你必须:

  1. Change the link to a pretty link:

    更改链接到一个漂亮的链接:

    <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.

    使用上面描述的任何方法,在服务器上使用mod_rewrite来处理对URL /my/pretty/link的请求。

(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.)

(我们可以使用mod_replace来转换传出的HTML页面及其包含的链接。尽管这比仅仅更新HTML资源要花费更多的精力。

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.

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

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

查看所有可能的标志和选项的官方文档。

#2


66  

To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.

为了扩展“欺骗”的答案,我想提供一些例子和一些其他mod_rewrite功能的解释。

All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.

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

Rewrite Example

Lets take this example:

让我们用这个例子:

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

The rule is split into 4 sections:

该规则分为4个部分:

  1. RewriteRule - starts the rewrite rule
  2. 重写—开始重写规则。
  3. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from
  4. ^博客/([0 - 9]+)/([A-Za-z0-9 - \ +]+)/ ?$ -这称为模式,不过我将把它称为规则的左手边—你想要重写的东西。
  5. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to
  6. 博客/ index . php ?id=$1&title=$2 -被称为替换,或重写规则的右端-您想要重写的内容。
  7. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later
  8. [NC,L,QSA]是重写规则的标志,用逗号分隔,稍后我将解释更多。

The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.

上面的重写会让你链接到像/blog/1/foo/这样的东西,它实际上会加载/blog/index.php?id=1&title=foo。

Left hand side of the rule

  • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...
  • ^表示开始页面的名称,所以它将改写example.com/blog/..。但不是example.com/foo/blog/..。
  • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:
    • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule
    • 第一组括号——([0-9]+)——匹配一个长度最小为1字符的字符串,并且只匹配数字值(即0-9)。这可以在规则右边的$1中引用。
    • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule
    • 第二组括号匹配长度最小为1字符的字符串,只包含字母数字字符(a-z、a-z或0-9)或-或+(注释+被反斜杠转义,因为没有转义,这将作为regex重复字符执行)。这可以在规则右边的$2中引用。
  • 每一组(…)括号表示一个正则表达式,我们可以将其捕获为规则右边的变量。在这个例子中:第一组括号-([0-9]+)-匹配一个长度最小为1字符的字符串,并且只匹配数字值(即0-9)。这可以引用1美元在规则的右手边第二组括号匹配字符串的最低1字符长度,只包含字母数字字符(a - z、a - z或0 - 9)或-或+(注意+用反斜杠转义为没有逃离这将作为一个正则表达式执行重复字符)。这可以在规则右边的$2中引用。
  • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place
  • 吗?意味着前面的字符是可选的,因此在本例中,两个/blog/1/foo/和/blog/1/foo将重写到相同的位置。
  • $ indicates this is the end of the string we want to match
  • $表示这是我们要匹配的字符串的结尾。

Flags

These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:

这些选项是在重写规则末尾的方括号中添加的,以指定特定的条件。同样,有很多不同的标志你可以在文档中看到,但是我要讲一些比较常见的标志:

NC

The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.

没有case标志意味着重写规则是不区分大小写的,因此对于上面的示例规则,这意味着两个/blog/1/foo/和/blog/1/foo/(或任何其他的变体)都将被匹配。

L

The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.

最后一个标志表示这是应该处理的最后一条规则。这意味着,如果且仅当此规则匹配时,在当前重写处理运行中不会对其他规则进行评估。如果规则不匹配,所有其他规则将按照惯例进行尝试。如果您没有设置L标志,那么所有下面的规则将被应用到重写后的URL。

END

Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)

因为Apache 2.4也可以使用[END]标志。匹配的规则将完全终止别名/重写处理。(然而,[L]标志通常可以触发第二轮,例如,当重写或退出子目录时)。

QSA

The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15

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

R

This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:

这个标志不是我在上面的例子中使用的,但是我认为它值得一提。这允许您指定一个http重定向,其中包含一个状态代码(例如,R=301)。例如,如果你想在/myblog/ to /blog上做一个301重定向,你可以简单地写一条这样的规则:

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

Rewrite Conditions

Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:

重写条件使重写更加强大,允许您为更具体的情况指定重写。在文档中有很多可以阅读的条件,但是我将介绍一些常见的例子并解释它们:

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

This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/

这是一种非常常见的做法,它将以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]

This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.

这有点不太常见,但是如果文件名是服务器上存在的目录或文件,那么它就是一个不执行规则的好例子。

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).
  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]只会在文件扩展为jpg、jpeg、gif或png(大小写不敏感)时执行重写。
  • %{REQUEST_FILENAME} !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't
  • %{REQUEST_FILENAME} !-f将检查该文件是否存在于当前服务器上,如果它不存在,则只执行重写。
  • %{REQUEST_FILENAME} !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't
  • %{REQUEST_FILENAME} !-d将检查该文件是否存在于当前服务器上,如果它不存在,则只执行重写。
  • The rewrite will attempt to load the same file on another domain
  • 重写将尝试在另一个域中加载相同的文件。

#3


35  

References

Stack Overflow has many other great resources to get started:

Stack Overflow还有很多其他的资源可以启动:

And newcomer-friendly regex overviews even:

而纽科姆友好的regex overviews甚至:

  • Our tag wiki for a syntax compendium.
  • 我们的regex标记wiki用于语法概要。
  • And the short Apache regex summary.
  • 以及简短的Apache regex概要。
  • Else regexp.info for easy-to-understand basics.
  • 其他的regexp.info用于容易理解的基础。

Oft-used placeholders

  • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.
  • .*匹配任何东西,即使是空字符串。您不希望在任何地方使用这种模式,但通常在最后的回退规则中使用。
  • [^/]+ is more often used for path segments. It matches anything but the forward slash.
  • [^ /]+经常用于路径段。它与前面的斜杠匹配。
  • \d+ only matches numeric strings.
  • \d+只匹配数字字符串。
  • \w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].
  • \ w +字母数字字符匹配。它基本上是[A-Za-z0-9_]的缩写。
  • [\w\-]+ for "slug"-style path segments, using letters, numbers, dash - and _
  • 用字母、数字、破折号和_来表示“slug”式的路径段。
  • [\w\-.,]+ adds periods and commas. Prefer an escaped \- dash in […] charclasses.
  • (\ w \ -。+添加句号和逗号。在[…]charclasses中更倾向于逃离的\- dash。
  • \. denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.
  • \。表示一段文字。否则。在[…]之外是任何符号的占位符。

Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.

每个占位符通常用(…)括号括起来作为捕获组。而整个模式通常是在…$ start + end标记。引用“模式”是可选的。

RewriteRules

The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases. They're just summaries, often link to more variations or detailed Q&As.

下面的示例是php为中心的,并且稍微增加一点,以适应类似的情况。他们只是总结,经常链接到更多的变化或详细的Q&As。

  • Static mapping
    /contact, /about

    Shortening a few page names to internal file schemes is most simple:

    将几个页面名称缩短为内部文件方案最简单:

     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:

    在现有的PHP脚本中引入诸如http://example.com/article/531这样的快捷方式也很简单。数字占位符可以重新映射到$_GET参数:

     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
     #                       └────────────────────────────────┘
    

    Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.

    注意,您的脚本必须能够(或被修改)将这些标题映射回数据库id。一个人不能凭空创造或猜测信息。

  • Slugs with numeric prefixes
    /readable/123-plus-title

    Therefore you'll often see mixed /article/529-title-slug paths used in practice:

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

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

    Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.

    现在您可以跳过标题=$2,因为您的脚本通常会依赖于数据库id。- titleslug已经成为任意的URL装饰。

  • Uniformity with alternative lists
    /foo/… /bar/… /baz/…

    If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:

    如果对多个虚拟页面路径有相似的规则,那么可以使用|替代列表对它们进行匹配和压缩。再把它们重新分配到内部得到参数

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

    You can split them out into individual RewriteRules should this get too complex.

    你可以把它们分成个别的重写,这样就太复杂了。

  • Dispatching related URLs to different backends
    /date/SWITCH/backend

    A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:

    另一种更实际的用法是映射请求路径到不同的脚本。例如,为较老的和基于日期的新web应用程序提供统一的url:

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

    This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler. Note the more specific rule coming first. Each script might use different GET params.

    这简单地将2009-2011年的文章重新映射到一个脚本上,而所有其他年份都隐式地映射到另一个处理程序。请注意,更具体的规则首先出现。每个脚本可能使用不同的GET参数。

  • Other delimiters than just / path slashes
    /user-123-name

    You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.

    您通常会看到RewriteRules模拟虚拟目录结构。但你并不是*缺乏创造力。你也可以用连字符来分割或构造。

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

    For the also common /wiki:section:Page_Name scheme:

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

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

    Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.

    偶尔也适用于/-分隔符和:或。同样的规则。或者有两个RewriteRules重新映射到不同的脚本。

  • Optional trailing / slash
    /dir = /dir/

    When opting for directory-style paths, you can make it reachable with and without a final /

    当选择directory风格的路径时,您可以使它可以在没有final /的情况下实现。

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

    Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.

    现在,它可以同时处理http://example.com/blog/123和/blog/123/。/ ?$方法很容易附加到任何其他的RewriteRule。

  • Flexible segments for virtual paths
    .*/.*/.*/.*

    Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however. The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:

    您将会遇到的大多数规则都映射到单个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
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each. Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.

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

    Alternatively you can utilize PHPs array parameters via ?p[]=$1&p[]=$2&p[]=3 query string here - if your script merely prefers them pre-split. (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)

    或者,您可以在这里使用PHPs数组参数?p[]=$1&p[]=$2&p[]=3查询字符串—如果您的脚本只是喜欢它们预先分割。(不过,使用catch-all规则更常见,并让脚本本身将段扩展到REQUEST_URI。)

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

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

  • Optional segments
    prefix/opt?/.*

    A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:

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

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

    Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.

    现在更复杂的模式(?:/([/])+)?简单地封装一个非捕获(?:…)组,并使其可选)?包含占位符([^ /]+)将替换模式2美元,但是是空的如果没有中间/…/路径。

  • Capture the remainder
    /prefix/123-capture/…/*/…whatever…

    As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.

    如前所述,您通常不需要太通用的重写模式。但是,将静态和特定的比较与a进行比较是有意义的。

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

    This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters itself (which is what Web-"MVC" frameworks do).

    这个选择了任何/…/…当然,这需要处理脚本将它们分割开来,并且变量-ify提取的参数本身(这就是Web-“MVC”框架所做的)。

  • Trailing file "extensions"
    /old/path.HTML

    URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image). However if you had a 1:1 file mapping before, you can craft simpler rules:

    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
    

    Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.

    其他常见的用途是将过时的.html路径重新映射到更新的.php处理程序,或者仅为单个(实际的/真实的)文件混叠目录名。

  • Ping-Pong (redirects and rewrites in unison)
    /ugly.html ←→ /pretty

    So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze. Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish the new URLs.

    因此,在某些时候,你正在重写你的HTML页面,只带一些漂亮的链接,如被欺骗。同时,你仍然会收到旧路径的请求,有时甚至是来自书签。作为变通方法,您可以使用ping-pong浏览器来显示/建立新的url。

    This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme. Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.

    这个常见的技巧是,每当传入的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]
    

    Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping query string parameters for example: Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops

    注意这个示例如何使用[END]而不是[L]安全地交替使用。对于较老的Apache 2.2版本,您可以使用其他的变通方法,而且还可以重新映射查询字符串参数:重定向到漂亮的URL,重新映射到丑陋的路径,没有无限循环。

  • Spaces in patterns
    /this+that+

    It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped \␣ spaces. Else just "-quote the whole pattern or substitution:

    在浏览器地址栏中并不是那么漂亮,但是你可以在url中使用空格。改写模式使用backslash-escaped \␣空间。其他只是"-引用整个模式或替换:

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

    Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.

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

Frequent duplicates:

频繁的重复:

  • Catch-all for a central dispatcher / front-controller script

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

    Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)

    它经常被PHP框架或WebCMS /门户脚本使用。然后,使用$_SERVER[“REQUEST_URI”]在PHP中处理实际的路径拆分。所以从概念上讲,这和URL处理“每个mod_rewrite”是完全相反的。(仅使用FallBackResource代替。)

  • Remove www. from hostname

    Note that this doesn't copy a query string along, etc.

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

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

    See also:
    · URL rewriting for different protocols in .htaccess
    · Generic htaccess redirect www to non-www
    · .htaccess - how to force "www." in a generic way?

    参见:·在.htaccess中对不同协议的URL重写·通用htaccess将www重定向到非www·htaccess——如何以通用的方式强制“www”?

    Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:

    注意,RewriteCond/RewriteRule combos可以更复杂,匹配(%1和$1)在两个方向上相互作用:

    参考:mod_rewrite, URL重写和“漂亮链接”解释。
    Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0

    Apache manual - mod_rewrite intro,版权2015 Apache Software Foundation, AL-2.0。

  • Redirect to HTTPS://

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

    See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS

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

  • "Removing" the PHP extension

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

    See also: Removing the .php extension with mod_rewrite

    请参见:通过mod_rewrite删除.php扩展。

  • Aliasing old .html paths to .php scripts

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

    见:http://httpd.apache.org/docs/2.4/rewrite/remapping.html向后兼容

  • Rewrite from URL like "/page" to a script such as "/index.php/page"

    See mod_rewrite, php and the .htaccess file

    请参阅mod_rewrite、php和.htaccess文件。

  • Redirect subdomain to a folder

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

    看看我如何能让我的htaccess工作(子域名)?

Prevalent .htaccess pitfalls

Now take this with a grain of salt. Not every advise can be generalized to all contexts. This is just a simple summary of well-known and a few unobvious stumbling blocks:

现在把这个带点盐。并不是所有的建议都可以推广到所有的场合。这只是对众所周知的和一些不明显的绊脚石的简单总结:

  • Enable mod_rewrite and .htaccess

    To actually use RewriteRules in per-directory configuration files you must:

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

    • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.

      检查您的服务器是否已经启用了所有启用的权限。否则,您的每个目录.htaccess指令将被忽略,而RewriteRules将不起作用。

    • Obviously have mod_rewrite enabled in your httpd.conf modules section.

      显然,在httpd中启用了mod_rewrite。配置模块部分。

    • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections, the per-directory .htaccess files need it individually summoned.

      使用RewriteEngine对每个规则的列表进行预处理。虽然mod_rewrite在 和 <目录> 段中隐式活动,但是每个目录.htaccess文件需要单独调用。

  • The leading slash ^/ won't match

    You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:

    你不应该开始你的。htaccess RewriteRule模式^ /通常:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.

    这在旧的教程中经常看到。它过去对古代Apache 1是正确的。x版本。现在,请求路径在.htaccess RewriteRules中是很方便的完全直接相关的。请离开前面/出去。

    · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.
    · Or when using a RewriteCond %{REQUEST_URI} you'd still match for a leading /.
    · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?

    注意,在 部分中,主斜杠仍然是正确的。这就是为什么你经常看到它^ / ?optionalized规则平价。·或者当使用RewriteCond %{REQUEST_URI}时,您仍然可以匹配一个引导/。参见网站管理员。在mod_rewrite模式中,什么时候是最需要的斜杠(/)?

  • <IfModule *> wrappers begone!

    You've probably seen this in many examples:

    你们可能在很多例子中见过这个例子:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).
    • 部分——如果它与另一个后备选项(如ScriptAliasMatch)组合在一起,它确实是有意义的。(但从来没有人这样做过)。
    • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.
    • 它通常用于默认的.htaccess规则集,其中包含许多开源项目。它的意思是作为回退,并保持“丑陋”的url作为默认。

    However you don't want that usually in your own .htaccess files.

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

    • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).
    • 首先,mod_rewrite不会随机地脱离。(如果是的话,你会有更大的麻烦)。
    • Were it really be disabled, your RewriteRules still wouldn't work anyway.
    • 如果它真的被禁用了,你的重写程序仍然不管用。
    • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)
    • 它的目的是防止HTTP 500错误。它通常实现的是用HTTP 404错误来优雅用户。(如果你仔细想想的话,就不是那么友好了。)
    • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.
    • 实际上,它只是抑制了更有用的日志条目,或者服务器通知邮件。你不知道为什么你的简历没有成功。

    What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.

    作为一种普遍的保障,看似诱人的东西,往往成为实践中的障碍。

  • Don't use RewriteBase unless needed

    Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.

    许多复制+粘贴示例包含一个RewriteBase /指令。这是默认的默认值。所以实际上你并不需要这个。它是一种用于高级虚拟主机重写方案的变通方案,以及一些共享的主机的DOCUMENT_ROOT路径。

    It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.

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

    See also How does RewriteBase work in .htaccess

    请参见如何在.htaccess中进行RewriteBase工作。

  • Disable MultiViews when virtual paths overlap

    URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might * with similar virtual RewriteRule paths.

    URL重写主要用于支持虚拟传入路径。通常,您只有一个dispatcher脚本(index.php)或一些单独的处理程序(文章)。php,博客。php,wiki。php,……)。后者可能会与类似的虚拟重写路径发生冲突。

    A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.

    例如,对/article/123的请求可以映射到文章。php带有/123 PATH_INFO隐式。你要么必须守住规则,然后再用普通的重写econd !-f+!-d和/或禁用PATH_INFO支持,或者只是禁用选项-多视图。

    Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.

    这并不是说你总是不得不这样做。内容协商只是虚拟资源的一种自动化。

  • Ordering is important

    See Everything you ever wanted to know about mod_rewrite if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed. You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.

    如果您还没有了解mod_rewrite,请查看您想要了解的所有内容。合并多个RewriteRules通常会导致交互。这并不是一种可以习惯性地阻止(L)标志的东西,而是一种你曾经熟悉的计划。您可以从一个规则重新重写虚拟路径到另一个规则,直到它到达一个实际的目标处理程序。

    Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules. Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)

    在早期的规则中,您常常希望拥有最具体的规则(固定的字符串/论坛/…模式,或者更严格的占位符[/.]+)。一般的“啜吸”规则(*)最好留给后面的规则。(一个例外是RewriteCond -f/-d guard作为主块。)

  • Stylesheets and images stop working

    When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>). Which can be solved by:

    当您引入虚拟目录结构/blog/article/123时,这会影响到HTML中的相对资源引用(例如参考:mod_rewrite, URL重写和“漂亮链接”解释。)。可以通过以下方式解决:

    • Only using server-absolute references href="/old.html" or src="/logo.png"
    • 仅使用server-absolute引用href="/old。html”或src = " / logo.png”
    • Often simply by adding <base href="/index"> into your HTML <head> section. This implicitly rebinds relative references to what they were before.
    • 通常只需将添加到HTML 部分。这隐含地将相对引用重新绑定到它们之前的内容。

    You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations. But that's both unneeded, or incurs extra redirects and hampers caching.

    您可以选择进一步修改RewriteRules以重新绑定。css或.png路径到它们的原始位置。但这是不需要的,或者是额外的重定向和妨碍缓存。

    See also: CSS, JS and images do not display with pretty url

    还可以看到:CSS、JS和图像不显示漂亮的url。

  • RewriteConds just mask one RewriteRule

    A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):

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

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

    Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.

    它不是默认的。您可以使用[S=2]标记对它们进行链锁。否则你得重复一遍。虽然有时您可以编写一个“倒转”的基本规则来(结束)早期的重写处理。

  • QUERY_STRING exempt from RewriteRules

    You can't match RewriteRule index.php\?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:

    你不能匹配RewriteRule index。php\?x=y,因为mod_rewrite相对于每个默认路径的相对路径。您可以通过以下方式分别匹配它们:

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

    See also How can I match query string variables with mod_rewrite?

    还可以看到如何使用mod_rewrite匹配查询字符串变量?

  • .htaccess vs. <VirtualHost>

    If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider moving rulesets into the vhost server configuration, once they've been battle-tested.

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

    In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server config files.

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

  • Whenever something doesn't work

    Fret not.

    烦恼。

    • Compare access.log and error.log

      比较访问。日志和error.log

      Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log. Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).

      通常情况下,你可以通过观察你的错误来判断一个RewriteRule的错误行为。日志和access.log。关联访问时间,以查看最初传入的请求路径,以及Apache无法解析的路径/文件(error 404/500)。

      This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further. Otherwise disable rules until you get some predictable paths.

      这并不能告诉你谁是罪犯。但是不可访问的最终路径如/docroot/21-.itle?index。php可能会给出进一步检查的地方。否则禁用规则,直到您获得一些可预测的路径。

    • Enable the RewriteLog

      使RewriteLog

      See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:

      看到Apache RewriteLog文档。对于调试,您可以在vhost部分启用它:

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

      That yields a detailed summary of how incoming request paths get modified by each rule:

      这就得到了每个规则如何修改传入请求路径的详细摘要:

      [..] 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'
      

      Which helps to narrow down overly generic rules and regex mishaps.

      这有助于缩小过于通用的规则和regex事故。

      See also:
      · .htaccess not working (mod_rewrite)
      · Tips for debugging .htaccess rewrite rules

      参见:·.htaccess不工作(mod_rewrite)·调试.htaccess重写规则的提示。

    • Before asking your own question

      在问你自己的问题之前。

      As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic by including prior research and attempts (avoid redundant answers), demonstrate basic understanding, and:

      您可能知道,Stack Overflow非常适合在mod_rewrite上询问问题。通过之前的研究和尝试(避免冗余的答案)使他们成为主题,展示基本的正则表达式理解,并且:

      • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.
      • 包括输入url的完整示例、错误重写的目标路径、真实的目录结构。
      • The complete RewriteRule set, but also single out the presumed defective one.
      • 完整的重写集,但也排除了假定有缺陷的一组。
      • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.
      • 如果是关于参数不匹配的,Apache和PHP版本、OS类型、文件系统、DOCUMENT_ROOT和PHPs $_SERVER环境。
      • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.
      • 摘录自你的访问。日志和错误。日志,以验证现有规则所决定的内容。更好的是,重写。日志总结。

      This nets quicker and more exact answers, and makes them more useful to others.

      这些网更快更准确的回答,使它们对别人更有用。

  • Comment your .htaccess

    If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution, it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be all the more interested in not treating them like magic blackboxes.

    如果您从某个地方复制示例,请注意包含一个#注释和原点链接。虽然省略属性仅仅是不礼貌的行为,但它往往会在以后伤害到维护。编写任何代码或教程源代码。尤其是当你不懂的时候,你应该更感兴趣的是不要把它们当作魔术的黑盒子。

  • It's not "SEO"-URLs

    Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.

    免责声明:只是一个宠物的问题。你经常听到漂亮的URL重写计划被称为“SEO”链接或其他东西。虽然这对于谷歌的例子来说很有用,但它是一种过时的用词不当。

    None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.

    在路径段中,没有任何一个现代搜索引擎真的被.html和.php打扰,或者?id=123查询字符串。旧的搜索引擎,如AltaVista,确实避免了爬行类网站的访问路径。现代的爬虫甚至常常渴望得到深层的网络资源。

    What "pretty" URLs should conceptionally be used for is making websites user-friendly.

    从概念上说,“漂亮”的url应该用来使网站变得友好。

    1. Having readable and obvious resource schemes.
    2. 具有可读和明显的资源计划。
    3. Ensuring URLs are long-lived (AKA permalinks).
    4. 确保url是长寿的(也就是permalinks)。
    5. Providing discoverability through /common/tree/nesting.
    6. 通过/共同/树/提供的可发现性嵌套。

    However don't sacrifice unique requirements for conformism.

    然而,不要为了因循守旧而牺牲独特的要求。

Tools

There are various online tools to generate RewriteRules for most GET-parameterish URLs:

有各种各样的在线工具来为大多数GET-parameterish url生成RewriteRules:

Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.

主要是输出[/]+一般的占位符,但可能满足琐碎的站点。

#4


5  

Alternatives to mod_rewrite

Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.

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

  1. Use the PATH_INFO, Luke

    Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:

    现在,在默认情况下,通常启用了AcceptPathInfo。它基本上允许。php和其他资源url进行虚拟参数:

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

    Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.

    现在,这个/虚拟/路径在PHP中显示为$_SERVER["PATH_INFO"],在这里您可以处理任何额外的参数。

    This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.

    这并不像将Apache单独的输入路径段分割为$1、$2、$3并将它们作为不同的$_GET变量传递给PHP那样方便。它只是模拟“漂亮的url”,而配置的工作量较少。

  2. Enable MultiViews to hide the .php extension

    The simplest option to also eschew .php "file extensions" in URLs is enabling:

    在url中,最简单的选项也可以避免。php“文件扩展”:

    Options +MultiViews
    

    This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.

    这有Apache select文章。php由于匹配的basename而在/文章上请求HTTP请求。这与前面提到的PATH_INFO特性很好地结合在一起。所以你可以使用http://example.com/article/virtual/title。如果您有一个具有多个PHP调用点/脚本的传统web应用程序,这是有意义的。

    Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).

    请注意,多视图具有不同的/更广泛的用途。它会带来非常小的性能损失,因为Apache总是会寻找其他具有匹配basenames的文件。它实际上是用于内容协商的,因此浏览器在可用资源(如article.en)中获得最佳替代。php,article.fr.php article.jp.mp4)。

  3. SetType or SetHandler for extensionless .php scripts

    A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:

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

    DefaultType application/x-httpd-php
    

    This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.

    这样您就可以重命名您的文章了。php脚本(不需要扩展),但仍将其作为php脚本处理。

    Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:

    现在,这可能会带来一些安全性和性能方面的影响,因为所有的extensionless文件都将通过PHP进行管道传输。因此,您可以选择将此行为设置为单独的文件:

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

    This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.

    这有点依赖于您的服务器设置和使用的PHP SAPI。常见的替代方案包括ForceType应用程序/x-httpd-php或AddHandler php5-script。

    Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.

    再次注意,这些设置从一个.htaccess传播到子文件夹。您总是应该禁用脚本执行(SetHandler None和Options -Exec或php_flag引擎等)用于静态资源、上传/目录等。

  4. Other Apache rewriting schemes

    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.

    在它的众多选项中,Apache提供了mod_alias特性,它有时和mod_rewrite RewriteRules一样有效。注意,大多数必须在 部分中设置,而不是在每个目录.htaccess配置文件中。

    • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.

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

    • And a plain Alias helps with a few simple rewriting schemes as well.

      简单的别名也可以帮助编写一些简单的重写方案。

    • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.

      即使是一个简单的ErrorDocument指令也可以用来让一个PHP脚本处理虚拟路径。请注意,这是一个笨拙的工作,但是,它禁止任何东西,但是可以得到请求,并将错误淹没。日志的定义。

    See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.

    请参阅http://httpd.apache.org/docs/2.2/urlmapp.html获取更多提示。