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

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

"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)

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

4 个解决方案

#1


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:

这是浏览器向Web服务器请求URL /foo/bar.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 OnRewriteRule   /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,它将被处理,就像请求了/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.

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

Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).

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

* If allowed by the primary Apache configuration file; it's optional, but often enabled.

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

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

<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_substitute来转换传出的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


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

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

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

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

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. RewriteRule - 启动重写规则

  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. ^ blog /([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. blog / 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个字符的字符串,仅包含字母数字字符(AZ,az或0-9)或 - 或+(注意+使用反斜杠进行转义,因为没有转义它将执行作为正则表达式的重复字符)。这可以在规则的右侧以$ 2引用

  • 每组(...)括号表示一个正则表达式,我们可以将其捕获为规则右侧的变量。在此示例中:第一组括号 - ([0-9] +) - 匹配长度最小为1个字符且仅包含数字值(即0-9)的字符串。这可以在规则的右侧用$ 1引用第二组括号匹配长度最少为1个字符的字符串,仅包含字母数字字符(AZ,az或0-9)或 - 或+( note +用反斜杠转义,因为没有转义,它将作为正则表达式重复字符执行)。这可以在规则的右侧以$ 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.

无案例标志意味着重写规则不区分大小写,因此对于上面的示例规则,这将意味着/ 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 /?comments = 15之类的内容会加载/blog/index.php?id=1&title=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 redirectRewriteCond %{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 domainRewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule (.*)$ 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


References

Stack Overflow has many other great resources to get started:

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

And newcomer-friendly regex overviews even:

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

  • Our tag wiki for a syntax compendium.
  • 我们的正则表达式标记维基用于语法概要。

  • And the short Apache regex summary.
  • 而简短的Apache正则表达式总结。

  • 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 _
  • [\ w \ - ] +“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 markers中。引用“模式”是可选的。

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为中心,稍微增量,更容易适应类似案例。它们只是摘要,通常链接到更多变体或详细的问答。

  • 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:

    将http://example.com/article/531等快捷方式引入现有PHP脚本也很容易。数字占位符可以重新映射到$ _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:

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

     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。仅凭RewriteRules无法凭空创建或猜测信息。

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

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

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

     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.

    现在你可以跳过传递title = $ 2,因为你的脚本通常依赖于数据库id。 -title-slug已成为任意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:

    如果您对多个虚拟页面路径有类似的规则,则可以使用|匹配并压缩它们替代名单。再次将它们重新分配给内部GET参数:

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

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

    如果这太复杂,你可以将它们分成单独的RewriteRules。

  • 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:

    对于common / 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.

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

  • Optional trailing / slash
    /dir = /dir/

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

    选择目录样式路径时,可以使用和不使用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正则表达式引擎不允许任意选择任意数量的选项。但您可以轻松地将其扩展为规则块:

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

    或者你可以通过?p [] = $ 1&p [] = $ 2&p [] = 3查询字符串来利用PHP数组参数 - 如果你的脚本只是喜欢它们预分割。(虽然更常见的是只使用一个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 containedplaceholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.

    现在更复杂的模式(?:/([^ /])+)?只是包装一个非捕获(?:...)组,并使其可选)? containsplaceholder([^ /] +)将是替换模式$ 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.

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

     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 parametersitself (which is what Web-"MVC" frameworks do).

    这个可选的任何/ ... / ... / ...尾随路径段。然后当然需要处理脚本将它们拆分,并且可变地提取自己的参数(这是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/establishthe new URLs.

    因此,在某些时候,您正在重写您的HTML页面以仅携带漂亮的链接,如deceze所述。同时您仍然会收到旧路径的请求,有时甚至是书签。作为解决方法,您可以使用乒乓浏览器来显示/建立新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 remappingquery 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中使用空格。对于重写模式,使用反斜杠转义\␣spaces.Else只是“ - 引用整个模式或替换:

     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.

    客户端使用+或%20序列化URL以获取空格。然而在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组合可能更复杂,匹配(%1和$ 1)甚至在两个方向上相互作用:

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

    Apache手册 - mod_rewrite简介,版权所有2015 The 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

  • 将旧的.html路径别名为.php脚本请参阅:http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility

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

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

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

      显然在httpd.conf模块部分中启用了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 On前置每个规则列表。虽然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?

    ·请注意,在 部分中,前导斜杠仍然正确。这就是你经常看到它的原因^ /?规则奇偶校验可选。 ·或者当使用RewriteCond%{REQUEST_URI}时,你仍然匹配一个领先的/。 ·另请参见Webmaster.SE: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.
    • 如果它真的被禁用,你的RewriteRules仍然无法正常工作。

    • 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.
    • 实际上,它只是抑制了更有用的日志条目或服务器通知邮件。对于为什么你的RewriteRules永远不会工作,你不是更明智的。

    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 /指令。无论如何,这恰好是隐式默认值。所以你实际上并不需要这个。这是一个花哨的VirtualHost重写方案的解决方法,并为一些共享的托管服务商误认为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应用程序是有意义的。在这种情况下,它可以缩短RewriteRule模式。通常,最好在每个目录的规则集中使用相对路径说明符。

    See also How does RewriteBase work in .htaccess

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

  • 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重写主要用于支持虚拟传入路径。通常你只有一个调度程序脚本(index.php)或一些单独的处理程序(articles.php,blog.php,wiki.php,...)。后者可能会与类似的虚拟RewriteRule路径发生冲突。

    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的请求可以隐式地映射到带有/ 123 PATH_INFO的article.php。您要么必须使用普通的RewriteCond保护您的规则!-f +! - d,和/或禁用PATH_INFO支持,或者只是禁用选项-MultiViews。

    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_rewriteif 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_rewriteif你想知道的一切。组合多个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.)

    您仍然经常希望在早期规则中拥有最具体的规则(固定字符串/论坛/ ...模式或更严格的占位符[^ /。] +)。通用slurp-all规则(。*)最好留给后来的。 (一个例外是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"
    • 仅使用服务器绝对引用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]标志链接它们。否则你将不得不重复它们。虽然有时您可以制定一个“反向”主要规则,以便[END]尽早重写处理。

  • 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 retainscompiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however considermoving rulesets into the vhost server configuration, once they've been battle-tested.

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

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

    在这种情况下,更喜欢可选的^ /?目录分隔符前缀。这允许在PerDir和serverconfig文件之间*移动RewriteRules。

  • Whenever something doesn't work

    Fret not.

    • Compare access.log and error.log

      比较access.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).

      通常你可以通过查看你的error.log和access.log.Correlate访问时间来看出RewriteRule是如何行为不端的,以查看最初进入的请求路径,以及Apache无法解析的路径/文件(错误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.

      这并不能告诉你哪个RewriteRule是罪魁祸首。但是像/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.2RewriteLogLevel 5RewriteLog /tmp/rewrite.log# Apache 2.4LogLevel 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.

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

      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-topicby 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.
      • 完整的RewriteRule设置,但也挑出假定的有缺陷的一个。

      • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.
      • Apache和PHP版本,操作系统类型,文件系统,DOCUMENT_ROOT和PHP $ _SERVER环境,如果它与参数不匹配有关。

      • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.
      • 来自access.log和error.log的摘录,用于验证现有规则解析的内容。更好的是,一个rewrite.log摘要。

      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 beall the more interested in not treating them like magic blackboxes.

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

  • 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是长期存在的(AKA永久链接)。

    5. Providing discoverability through /common/tree/nesting.
    6. 通过/ common / tree / nesting提供可发现性。

    However don't sacrifice unique requirements for conformism.

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

Tools

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

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

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

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

#4


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.

可以在不使用RewriteRules的情况下实现许多基本的虚拟URL方案。 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 On。这基本上允许.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.

    现在这个/ virtual / path在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 / article上的HTTP请求选择了article.php。这与上述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).

    请注意,MultiViews具有不同/更广泛的用途。它会导致非常小的性能损失,因为Apache总是查找具有匹配基本名称的其他文件。它实际上是用于内容协商,因此浏览器在可用资源中获得最佳替代方案(例如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.

    这样你就可以将你的article.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:

    现在这可能会带来一些安全性和性能影响,因为所有无扩展文件现在都将通过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 application / 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_rewrites 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.

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

    • 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脚本处理虚拟路径。请注意,这是一个kludgy解决方法,但是,除了GET请求之外什么都禁止,并且根据定义泛滥error.log。

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

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

#1


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:

这是浏览器向Web服务器请求URL /foo/bar.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 OnRewriteRule   /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,它将被处理,就像请求了/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.

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

Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).

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

* If allowed by the primary Apache configuration file; it's optional, but often enabled.

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

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

<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_substitute来转换传出的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


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

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

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

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

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. RewriteRule - 启动重写规则

  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. ^ blog /([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. blog / 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个字符的字符串,仅包含字母数字字符(AZ,az或0-9)或 - 或+(注意+使用反斜杠进行转义,因为没有转义它将执行作为正则表达式的重复字符)。这可以在规则的右侧以$ 2引用

  • 每组(...)括号表示一个正则表达式,我们可以将其捕获为规则右侧的变量。在此示例中:第一组括号 - ([0-9] +) - 匹配长度最小为1个字符且仅包含数字值(即0-9)的字符串。这可以在规则的右侧用$ 1引用第二组括号匹配长度最少为1个字符的字符串,仅包含字母数字字符(AZ,az或0-9)或 - 或+( note +用反斜杠转义,因为没有转义,它将作为正则表达式重复字符执行)。这可以在规则的右侧以$ 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.

无案例标志意味着重写规则不区分大小写,因此对于上面的示例规则,这将意味着/ 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 /?comments = 15之类的内容会加载/blog/index.php?id=1&title=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 redirectRewriteCond %{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 domainRewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule (.*)$ 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


References

Stack Overflow has many other great resources to get started:

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

And newcomer-friendly regex overviews even:

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

  • Our tag wiki for a syntax compendium.
  • 我们的正则表达式标记维基用于语法概要。

  • And the short Apache regex summary.
  • 而简短的Apache正则表达式总结。

  • 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 _
  • [\ w \ - ] +“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 markers中。引用“模式”是可选的。

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为中心,稍微增量,更容易适应类似案例。它们只是摘要,通常链接到更多变体或详细的问答。

  • 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:

    将http://example.com/article/531等快捷方式引入现有PHP脚本也很容易。数字占位符可以重新映射到$ _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:

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

     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。仅凭RewriteRules无法凭空创建或猜测信息。

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

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

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

     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.

    现在你可以跳过传递title = $ 2,因为你的脚本通常依赖于数据库id。 -title-slug已成为任意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:

    如果您对多个虚拟页面路径有类似的规则,则可以使用|匹配并压缩它们替代名单。再次将它们重新分配给内部GET参数:

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

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

    如果这太复杂,你可以将它们分成单独的RewriteRules。

  • 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:

    对于common / 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.

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

  • Optional trailing / slash
    /dir = /dir/

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

    选择目录样式路径时,可以使用和不使用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正则表达式引擎不允许任意选择任意数量的选项。但您可以轻松地将其扩展为规则块:

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

    或者你可以通过?p [] = $ 1&p [] = $ 2&p [] = 3查询字符串来利用PHP数组参数 - 如果你的脚本只是喜欢它们预分割。(虽然更常见的是只使用一个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 containedplaceholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.

    现在更复杂的模式(?:/([^ /])+)?只是包装一个非捕获(?:...)组,并使其可选)? containsplaceholder([^ /] +)将是替换模式$ 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.

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

     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 parametersitself (which is what Web-"MVC" frameworks do).

    这个可选的任何/ ... / ... / ...尾随路径段。然后当然需要处理脚本将它们拆分,并且可变地提取自己的参数(这是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/establishthe new URLs.

    因此,在某些时候,您正在重写您的HTML页面以仅携带漂亮的链接,如deceze所述。同时您仍然会收到旧路径的请求,有时甚至是书签。作为解决方法,您可以使用乒乓浏览器来显示/建立新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 remappingquery 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中使用空格。对于重写模式,使用反斜杠转义\␣spaces.Else只是“ - 引用整个模式或替换:

     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.

    客户端使用+或%20序列化URL以获取空格。然而在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组合可能更复杂,匹配(%1和$ 1)甚至在两个方向上相互作用:

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

    Apache手册 - mod_rewrite简介,版权所有2015 The 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

  • 将旧的.html路径别名为.php脚本请参阅:http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility

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

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

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

      显然在httpd.conf模块部分中启用了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 On前置每个规则列表。虽然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?

    ·请注意,在 部分中,前导斜杠仍然正确。这就是你经常看到它的原因^ /?规则奇偶校验可选。 ·或者当使用RewriteCond%{REQUEST_URI}时,你仍然匹配一个领先的/。 ·另请参见Webmaster.SE: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.
    • 如果它真的被禁用,你的RewriteRules仍然无法正常工作。

    • 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.
    • 实际上,它只是抑制了更有用的日志条目或服务器通知邮件。对于为什么你的RewriteRules永远不会工作,你不是更明智的。

    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 /指令。无论如何,这恰好是隐式默认值。所以你实际上并不需要这个。这是一个花哨的VirtualHost重写方案的解决方法,并为一些共享的托管服务商误认为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应用程序是有意义的。在这种情况下,它可以缩短RewriteRule模式。通常,最好在每个目录的规则集中使用相对路径说明符。

    See also How does RewriteBase work in .htaccess

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

  • 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重写主要用于支持虚拟传入路径。通常你只有一个调度程序脚本(index.php)或一些单独的处理程序(articles.php,blog.php,wiki.php,...)。后者可能会与类似的虚拟RewriteRule路径发生冲突。

    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的请求可以隐式地映射到带有/ 123 PATH_INFO的article.php。您要么必须使用普通的RewriteCond保护您的规则!-f +! - d,和/或禁用PATH_INFO支持,或者只是禁用选项-MultiViews。

    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_rewriteif 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_rewriteif你想知道的一切。组合多个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.)

    您仍然经常希望在早期规则中拥有最具体的规则(固定字符串/论坛/ ...模式或更严格的占位符[^ /。] +)。通用slurp-all规则(。*)最好留给后来的。 (一个例外是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"
    • 仅使用服务器绝对引用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]标志链接它们。否则你将不得不重复它们。虽然有时您可以制定一个“反向”主要规则,以便[END]尽早重写处理。

  • 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 retainscompiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however considermoving rulesets into the vhost server configuration, once they've been battle-tested.

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

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

    在这种情况下,更喜欢可选的^ /?目录分隔符前缀。这允许在PerDir和serverconfig文件之间*移动RewriteRules。

  • Whenever something doesn't work

    Fret not.

    • Compare access.log and error.log

      比较access.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).

      通常你可以通过查看你的error.log和access.log.Correlate访问时间来看出RewriteRule是如何行为不端的,以查看最初进入的请求路径,以及Apache无法解析的路径/文件(错误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.

      这并不能告诉你哪个RewriteRule是罪魁祸首。但是像/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.2RewriteLogLevel 5RewriteLog /tmp/rewrite.log# Apache 2.4LogLevel 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.

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

      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-topicby 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.
      • 完整的RewriteRule设置,但也挑出假定的有缺陷的一个。

      • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.
      • Apache和PHP版本,操作系统类型,文件系统,DOCUMENT_ROOT和PHP $ _SERVER环境,如果它与参数不匹配有关。

      • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.
      • 来自access.log和error.log的摘录,用于验证现有规则解析的内容。更好的是,一个rewrite.log摘要。

      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 beall the more interested in not treating them like magic blackboxes.

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

  • 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是长期存在的(AKA永久链接)。

    5. Providing discoverability through /common/tree/nesting.
    6. 通过/ common / tree / nesting提供可发现性。

    However don't sacrifice unique requirements for conformism.

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

Tools

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

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

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

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

#4


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.

可以在不使用RewriteRules的情况下实现许多基本的虚拟URL方案。 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 On。这基本上允许.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.

    现在这个/ virtual / path在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 / article上的HTTP请求选择了article.php。这与上述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).

    请注意,MultiViews具有不同/更广泛的用途。它会导致非常小的性能损失,因为Apache总是查找具有匹配基本名称的其他文件。它实际上是用于内容协商,因此浏览器在可用资源中获得最佳替代方案(例如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.

    这样你就可以将你的article.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:

    现在这可能会带来一些安全性和性能影响,因为所有无扩展文件现在都将通过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 application / 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_rewrites 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.

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

    • 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脚本处理虚拟路径。请注意,这是一个kludgy解决方法,但是,除了GET请求之外什么都禁止,并且根据定义泛滥error.log。

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

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