简体   繁体   English

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

[英]Reference: mod_rewrite, URL rewriting and "pretty links" explained

"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. mod_rewrite是制作“漂亮链接”的一种方法,但它很复杂,而且它的语法非常简洁,难以理解,并且文档假定对 HTTP 有一定的熟练程度。 Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?有人可以简单地解释“漂亮链接”如何工作以及如何使用 mod_rewrite 来创建它们吗?

Other common names, aliases, terms for clean URLs: RESTful URLs, user-friendly URLs, SEO -friendly URLs, slugging , and MVC URLs (probably a misnomer)其他通用名称、别名、干净 URL 的术语: RESTful URL、用户友好 URL、 SEO友好 URL、 slugging和 MVC URL(可能用词不当)

To understand what mod_rewrite does you first need to understand how a web server works.要了解 mod_rewrite 是什么,您首先需要了解 Web 服务器的工作原理。 A web server responds to HTTP requests . Web 服务器响应HTTP 请求 An HTTP request at its most basic level looks like this:最基本的 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.这是浏览器向 Web 服务器请求URL /foo/bar.html的简单请求。 It is important to stress that it does not request a file , it requests just some arbitrary URL.重要的是要强调它不请求文件,它只请求一些任意 URL。 The request may also look like this:请求也可能如下所示:

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. Web 服务器是一个侦听端口的应用程序,接受来自该端口的 HTTP 请求并返回响应。 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. Web 服务器可以完全自由地以它认为合适的任何方式/以您配置的任何方式响应任何请求。 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.这个响应不是一个文件,它是一个HTTP 响应,它可能与任何磁盘上的物理文件有任何关系,也可能没有任何关系。 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. Web 服务器不一定是 Apache,还有许多其他的 Web 服务器,它们都是持续运行的程序,并连接到响应 HTTP 请求的端口。 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.本段旨在让您摆脱 URL 直接等于文件的任何概念,理解这一点非常重要。 :) :)

The default configuration of most web servers is to look for a file that matches the URL on the hard disk.大多数 Web 服务器的默认配置是在硬盘上查找与 URL 匹配的文件。 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.如果服务器的文档根目录设置为/var/www ,它可能会查看文件/var/www/foo/bar.html是否存在,如果存在则提供它。 If the file ends in ".php" it will invoke the PHP interpreter and then return the result.如果文件以“.php”结尾,它将调用 PHP 解释器,然后返回结果。 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.一个文件不必以“.php”结尾,Web 服务器就可以通过 PHP 解释器运行它,并且 URL 不必匹配磁盘上的任何特定文件以发生某些事情。

mod_rewrite is a way to rewrite the internal request handling. mod_rewrite 是一种重写内部请求处理的方法。 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.当 Web 服务器收到对 URL /foo/bar的请求时,您可以将该 URL重写为其他内容,然后 Web 服务器将在磁盘上查找与之匹配的文件。 Simple example:简单的例子:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz".这条规则说,只要请求匹配“/foo/bar”,就将其重写为“/foo/baz”。 The request will then be handled as if /foo/baz had been requested instead.然后将处理该请求,就好像/foo/baz已被请求一样。 This can be used for various effects, for example:这可用于各种效果,例如:

RewriteRule (.*) $1.html

This rule matches anything ( .* ) and captures it ( (..) ), then rewrites it to append ".html".此规则匹配任何内容( .* )并捕获它( (..) ),然后将其重写为附加“.html”。 In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested.换句话说,如果/foo/bar是请求的 URL,它将被处理为好像/foo/bar.html已被请求。 See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.有关正则表达式匹配、捕获和替换的更多信息,请参阅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.这再次匹配任何内容并将其重写为文件 index.php,并将最初请求的 URL 附加在url查询参数中。 Ie, 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 并且该文件将可以访问$_GET['url']中的原始请求,因此它可以用它做任何事情。

Primarily you put these rewrite rules into your web server configuration file .首先,您将这些重写规则放入您的Web 服务器配置文件中。 Apache also allows* you to put them into a file called .htaccess within your document root (ie next to your .php files). Apache 还允许*您将它们放入文档根目录中名为.htaccess的文件中(即在 .php 文件旁边)。

* If allowed by the primary Apache configuration file; *如果主 Apache 配置文件允许; it's optional, but often enabled.它是可选的,但经常启用。

What mod_rewrite does not do mod_rewrite不做什么

mod_rewrite does not magically make all your URLs "pretty". mod_rewrite 不会神奇地使您的所有 URL 都“漂亮”。 This is a common misunderstanding.这是一个常见的误解。 If you have this link in your web site:如果您的网站中有此链接:

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

there's nothing mod_rewrite can do to make that pretty.没有什么 mod_rewrite 可以做的漂亮。 In order to make this a pretty link, you have to:为了使它成为一个漂亮的链接,您必须:

  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. mod_rewrite 可以做很多事情,您可以创建非常复杂的匹配规则,包括链接多个重写、将请求代理到完全不同的服务或机器、返回特定的 HTTP 状态代码作为响应、重定向请求等。它非常强大,可用于如果您了解基本的 HTTP 请求-响应机制,那就太好了。 It does not automatically make your links pretty.不会自动使您的链接漂亮。

See the official documentation for all the possible flags and options.有关所有可能的标志和选项,请参阅官方文档

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 RewriteRule - 启动重写规则
  2. ^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 ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - 这称为模式,但我将其称为左侧规则的 - 你想要重写的内容
  3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to blog/index.php?id=$1&title=$2 - 称为替换,或重写规则的右侧 - 您要重写的内容
  4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later [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 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 (ie 0-9).第一组括号 - ([0-9]+) - 匹配长度至少为 1 个字符且仅具有数值(即 0-9)的字符串。 This can be referenced with $1 in the right hand side of the rule这可以用规则右侧的$1来引用
    • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (AZ, az, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character ).第二组括号匹配长度至少为 1 个字符的字符串,仅包含字母数字字符(AZ、az 或 0-9)或-+ (注意+用反斜杠转义,因为没有转义它会执行作为正则表达式重复字符)。 This can be referenced with $2 in the right hand side of the rule这可以用规则右侧的$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. no case 标志意味着重写规则不区分大小写,因此对于上面的示例规则,这意味着/blog/1/foo//BLOG/1/foo/ (或任何变体)都将匹配。

L

The last flag indicates that this is the last rule that should be processed.最后一个标志表明这是应处理的最后一条规则。 This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run.这意味着当且仅当此规则匹配时,不会在当前重写处理运行中评估进一步的规则。 If the rule does not match, all other rules will be tried in order as usual.如果规则不匹配,将照常尝试所有其他规则。 If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.如果您不设置L标志,则以下所有规则将应用于之后重写的 URL。

END

Since Apache 2.4 you can also use the [END] flag.从 Apache 2.4 开始,您还可以使用[END]标志。 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.) (而[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.查询字符串附加标志允许我们将额外的变量传递给指定的 URL,这些变量将被添加到原始 get 参数中。 For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15对于我们的示例,这意味着/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 (eg R=301 ).这允许您指定 http 重定向,并可选择包含状态代码(例如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:例如,如果您想在 /myblog/ 上执行 301 重定向到 /blog/,您只需编写如下规则:

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

Rewrite Conditions重写条件

Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. 重写条件使重写更加强大,允许您为更具体的情况指定重写。 There are a lot of conditions which you can read about in the documentation , but I'll touch on a few common examples and explain them:您可以在文档中阅读很多条件,但我将介绍一些常见示例并进行解释:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

This is a very common practice, which will prepend your domain with www.这是一种非常常见的做法,它会在您的域前面加上www. (if it isn't there already) and execute a 301 redirect. (如果它不存在)并执行 301 重定向。 For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/例如,加载http://example.com/blog/它会将您重定向到http://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.这稍微不太常见,但是如果文件名是服务器上存在的目录或文件,则不执行规则的一个很好的示例。

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

References参考

Stack Overflow has many other great resources to get started: Stack Overflow 还有许多其他很棒的入门资源:

And newcomer-friendly regex overviews even:甚至对新手友好的正则表达式概述:

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. \w+匹配字母数字字符。 It's basically shorthand for [A-Za-z0-9_] .它基本上是[A-Za-z0-9_]的简写。
  • [\w\-]+ for "slug"-style path segments, using letters, numbers, dash - and _ [\w\-]+用于“slug”风格的路径段,使用字母、数字、破折号-_
  • [\w\-.,]+ adds periods and commas. [\w\-.,]+添加句点和逗号。 Prefer an escaped \- dash in […] charclasses.更喜欢在[…]字符类中使用转义的\-破折号。
  • \. 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.引用“模式”是可选的。

RewriteRules重写规则

The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.以下示例以 PHP 为中心,并且更加增量,更容易适应类似情况。 They're just summaries, often link to more variations or detailed Q&As.它们只是摘要,通常链接到更多变体或详细问答。

  • Static mapping静态映射
    /contact , /about /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.http://example.com/article/531类的快捷方式引入现有 PHP 脚本也很容易。 The numeric placeholder can just be remapped to a $_GET parameter:数字占位符可以重新映射到$_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.请注意,您的脚本必须能够(或适应)将这些标题映射回数据库 ID。 RewriteRules alone can't create or guess information out of thin air.单独的 RewriteRules 不能凭空创建或猜测信息。

  • Slugs with numeric prefixes带有数字前缀的蛞蝓
    /readable/123-plus-title

    Therefore you'll often see mixed /article/529-title-slug paths used in practice:因此,您会经常看到在实践中使用的混合/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.现在您无论如何都可以跳过传递title=$2 ,因为您的脚本通常会依赖于 database-id 。 The -title-slug has become arbitrary URL decoration. -title-slug已成为任意 URL 装饰。

  • Uniformity with alternative lists与替代列表的一致性
    /foo/… /bar/… /baz/… /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 RewriteRule s should this get too complex.如果这变得太复杂,您可以将它们拆分为单独的RewriteRule

  • Dispatching related URLs to different backends将相关 URL 分派到不同的后端
    /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.这只是将 2009-2011 年的帖子重新映射到一个脚本,并将所有其他年份隐式地重新映射到另一个处理程序。 Note the more specific rule coming first .请注意更具体的规则首先出现 Each script might use different GET params.每个脚本可能使用不同的 GET 参数。

  • Other delimiters than just / path slashes除了/路径斜杠之外的其他分隔符
    /user-123-name

    You're most commonly seeing RewriteRules to simulate a virtual directory structure.您最常看到 RewriteRules 来模拟虚拟目录结构。 But you're not forced to be uncreative.但你不会被迫缺乏创造力。 You can as well use - hyphens for segmenting or structure.您也可以使用-连字符进行分段或结构。

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

    For the also common /wiki:section:Page_Name scheme:对于同样常见的/wiki:section:Page_Name方案:

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

    Occasionally it's suitable to alternate between / -delimiters and : or .有时它适合在/ -delimiters 和:或 之间交替使用. in the same rule even.甚至在同一规则中。 Or have two RewriteRules again to map variants onto different scripts.或者再次使用两个 RewriteRules 将变体映射到不同的脚本。

  • Optional trailing / slash可选的尾随/斜杠
    /dir = /dir/ /dir = /dir/

    When opting for directory-style paths, you can make it reachable with and without a final /选择目录式的路径时,您可以在有没有最终的情况下达到它

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

    Now this handles both http://example.com/blog/123 and /blog/123/ .现在这处理http://example.com/blog/123/blog/123/ And the /?$ approach is easy to append onto any other RewriteRule.并且/?$方法很容易附加到任何其他 RewriteRule 上。

  • Flexible segments for virtual paths虚拟路径的灵活段
    .*/.*/.*/.*

    Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters.您将遇到的大多数规则将一组受限的/…/资源路径段映射到单个 GET 参数。 Some scripts handle a variable number of options however.然而,一些脚本处理可变数量的选项 The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. Apache regexp 引擎不允许选择任意数量的它们。 But you can easily expand it into a rule block yourself:但是您可以自己轻松地将其扩展为规则块:

     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.或者,您可以在此处通过?p[]=$1&p[]=$2&p[]=3查询字符串使用 PHP 数组参数 - 如果您的脚本只是更喜欢预先拆分它们。 (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.) (虽然更常见的是只使用一个包罗万象的规则,并让脚本本身将这些段从 REQUEST_URI 中扩展出来。)

    See also: How do I transform my URL path segments into query string key-value pairs?另请参阅:如何将我的 URL 路径段转换为查询字符串键值对?

  • Optional segments可选段
    prefix/opt?/.*

    A common variation is to have optional prefixes within a rule.一个常见的变体是规则中具有可选前缀。 This usually makes sense if you have static strings or more constrained placeholders around:如果您有静态字符串或更受限制的占位符,这通常是有意义的:

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

    Now the more complex pattern (?:/([^/])+)?现在更复杂的模式(?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?那里简单地包装了一个非捕获(?:…)组,并使其成为可选的)? . . The contained placeholder ([^/]+) would be substitution pattern $2 , but be empty if there's no middle /…/ path.包含的占位符([^/]+)将是替换模式$2 ,但如果没有中间/…/路径,则为空。

  • Capture the remainder捕获剩余部分
    /prefix/123-capture/…/*/…whatever…

    As said before, you don't often want too generic rewrite patterns.如前所述,您通常不需要过于通用的重写模式。 It does however make sense to combine static and specific comparisons with a .* sometimes.但是,有时将静态和特定比较与.*结合起来确实有意义。

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

    This optionalized any /…/…/… trailing path segments.这可选择任何/…/…/…尾随路径段。 Which then of course requires the handling script to split them up, and variabl-ify extracted parameters itself (which is what Web-"MVC" frameworks do).然后当然需要处理脚本将它们拆分,并对提取的参数本身进行可变化(这就是Web- “MVC”框架所做的)。

  • Trailing file "extensions"尾随文件“扩展名”
    /old/path.HTML

    URLs don't really have file extensions. URL 并没有真正的文件扩展名。 Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).这就是整个参考的内容(= URL 是虚拟定位器,不一定是直接文件系统映像)。 However if you had a 1:1 file mapping before, you can craft simpler rules:但是,如果您之前有 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 /ugly.html ←→ /pretty

    So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze .因此,在某些时候,您正在重写 HTML 页面以仅携带漂亮的链接,如deceze 所述 Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks.同时,您仍然会收到对路径的请求,有时甚至是来自书签的请求。 As workaround , you can ping-pong browsers to display/establish the new URLs.作为解决方法,您可以通过 ping-pong 浏览器显示/建立新的 URL。

    This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.这个常见的技巧涉及每当传入的 URL 遵循过时/丑陋的命名方案时发送 30x/Location重定向 Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new 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.请注意此示例如何仅使用[END]而不是[L]来安全地交替。 For older Apache 2.2 versions you can use other workarounds, besides also remapping query string parameters for example: Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops对于较旧的 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.它在浏览器地址栏中不是那么漂亮,但您可以在 URL 中使用空格。 For rewrite patterns use backslash-escaped \␣ spaces.对于重写模式,使用反斜杠转义的\␣空格。 Else just " -quote the whole pattern or substitution:否则只需" - 引用整个模式或替换:

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

    Clients serialize URLs with + or %20 for spaces.客户端使用+%20对空格进行序列化。 Yet in RewriteRules they're interpreted with literal characters for all relative path segments.然而在 RewriteRules 中,它们被解释为所有相对路径段的文字字符。

Frequent duplicates:频繁重复:

Prevalent .htaccess pitfalls普遍存在.htaccess陷阱

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启用mod_rewrite.htaccess

    To actually use RewriteRules in per-directory configuration files you must:要在每个目录的配置文件中实际使用 RewriteRules,您必须:

    • Check that your server has AllowOverride All enabled .检查您的服务器是否启用了AllowOverride All Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.否则,您的每个目录的.htaccess指令将被忽略,并且 RewriteRules 将不起作用。

    • Obviously have mod_rewrite enabled in your httpd.conf modules section.显然在您的httpd.conf模块部分中启用了mod_rewrite

    • Prepend each list of rules with RewriteEngine On still.仍然在每个规则列表前面加上RewriteEngine On While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections, the per-directory .htaccess files need it individually summoned.虽然 mod_rewrite 在<VirtualHost><Directory>部分中隐式处于活动状态,但每个目录的.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.它曾经对古老的 Apache 1.x 版本是正确的。 Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules.如今,请求路径在.htaccess RewriteRules 中很方便地完全与目录相关 Just leave the leading / out.只留下领先/出局。

    · Note that the leading slash is still correct in <VirtualHost> sections though. · 请注意,虽然<VirtualHost>部分中的前导斜杠仍然是正确的。 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 / . · 或者当使用RewriteCond %{REQUEST_URI}时,您仍然会匹配前导/
    · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns? · 另请参阅Webmaster.SE:mod_rewrite 模式中何时需要前导斜杠 (/)?

  • <IfModule *> wrappers begone! <IfModule *>包装器消失了!

    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.<VirtualHost>部分中确实有意义 - 如果它与另一个后备选项结合使用,例如 ScriptAliasMatch。 (But nobody ever does that). (但从来没有人这样做过)。
    • And it's commonly distributed for default .htaccess rulesets with many open source projects.它通常用于许多开源项目的默认.htaccess规则集。 There it's just meant as fallback, and keeps "ugly" URLs work as default.在那里它只是作为后备,并保持“丑陋”的 URL 作为默认工作。

    However you don't want that usually in your own .htaccess files.但是,您通常不希望在您自己的.htaccess文件中使用它。

    • Firstly, mod_rewrite does not randomly disengage.首先, mod_rewrite 不会随机脱离。 (If it did, you'd have bigger problems). (如果是这样,你会有更大的问题)。
    • Were it really be disabled, your RewriteRules still wouldn't work anyway.如果它真的被禁用,你的 RewriteRules 仍然无法正常工作。
    • It's meant to prevent HTTP 500 errors.它旨在防止 HTTP 500错误。 What it usually accomplishes is gracing your users with HTTP 404 errors instead.它通常完成的是用 HTTP 404错误代替您的用户。 (Not so much more user-friendly if you think about it.) (如果您考虑一下,它不会对用户更加友好。)
    • 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除非需要,否则不要使用RewriteBase

    Many copy+paste examples contain a RewriteBase / directive.许多复制+粘贴示例包含RewriteBase /指令。 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.这是花哨的 VirtualHost 重写方案的解决方法,并且错误地猜测了某些共享主机的 DOCUMENT_ROOT 路径。

    It makes sense to use with individual web applications in deeper subdirectories.在更深的子目录中与单个 Web 应用程序一起使用是有意义的。 It can shorten RewriteRule patterns in such cases.在这种情况下,它可以缩短 RewriteRule 模式。 Generally it's best to prefer relative path specifiers in per-directory rule sets.通常,最好在每个目录规则集中使用相对路径说明符。

    See also How does RewriteBase work in .htaccess另请参阅RewriteBase 如何在 .htaccess 中工作

  • Disable MultiViews when virtual paths overlap虚拟路径重叠时禁用MultiViews

    URL rewriting is primarily used for supporting virtual incoming paths. URL 重写主要用于支持虚拟传入路径。 Commonly you just have one dispatcher script ( index.php ) or a few individual handlers ( articles.php , blog.php , wiki.php , …).通常,您只有一个调度程序脚本( index.php )或几个单独的处理程序( articles.phpblog.phpwiki.php ,...)。 The latter might clash with similar virtual RewriteRule paths.后者可能与类似的虚拟 RewriteRule 路径发生冲突

    A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly.例如,对/article/123的请求可以使用/123 PATH_INFO 隐式映射到article.php 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 .您要么必须使用普通的RewriteCond !-f + !-d来保护您的规则,和/或禁用 PATH_INFO 支持,或者只是禁用Options -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_rewrite if you haven't already.如果您还没有,请查看您想知道的关于 mod_rewrite 的所有信息 Combining multiple RewriteRules often leads to interaction.组合多个 RewriteRules 通常会导致交互。 This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.这不是每个[L]标志习惯性地阻止的事情,而是您一旦精通就会接受的方案。 You can re-re-re write virtual paths from one rule to another, until it reaches an actual target handler.可以重新重新编写从一条规则到另一条规则的虚拟路径,直到它到达实际的目标处理程序。

    Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+ ) in the early rules.尽管如此,您仍然经常希望在早期规则中拥有最具体的规则(固定字符串/forum/…模式,或更严格的占位符[^/.]+ )。 Generic slurp-all rules ( .* ) are better left to the later ones.通用的 slurp-all 规则 ( .* ) 最好留给后面的规则。 (An exception is a RewriteCond -f/-d guard as primary block.) (一个例外是RewriteCond -f/-d保护作为主要块。)

  • 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> ).当您引入虚拟目录结构/blog/article/123时,这会影响 HTML 中的相对资源引用(例如<img src=mouse.png> )。 Which can be solved by:可以通过以下方式解决:

    • 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.通常只需将<base href="/index">添加到您的 HTML <head>部分。 This implicitly rebinds relative references to what they were before.这隐含地将相对引用重新绑定到它们之前的内容。

    You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.您也可以制作进一步的 RewriteRules 以将.css.png路径重新绑定到其原始位置。 But that's both unneeded, or incurs extra redirects and hampers caching.但这都是不必要的,或者会导致额外的重定向并妨碍缓存。

    See also: CSS, JS and images do not display with pretty url另请参阅: CSS、JS 和图像不显示漂亮的 url

  • RewriteConds just mask one RewriteRule RewriteConds 只屏蔽一个 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.您可以使用[S=2]标志链接它们 Else you'll have to repeat them.否则,您将不得不重复它们。 While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.虽然有时您可以制定一个“倒置”的主要规则来提早 [END] 重写处理。

  • QUERY_STRING exempt from RewriteRules QUERY_STRING 免于 RewriteRules

    You can't match RewriteRule index.php\?x=y , because mod_rewrite compares just against relative paths per default.您无法匹配RewriteRule index.php\?x=y ,因为 mod_rewrite 仅与默认情况下的相对路径进行比较。 You can match them separately however via:您可以通过以下方式单独匹配它们:

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

    If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless.如果您在每个目录的配置文件中使用 RewriteRules,那么担心正则表达式的性能是没有意义的。 Apache retains compiled PCRE patterns longer than a PHP process with a common routing framework. Apache 保留编译后的 PCRE 模式的时间比使用通用路由框架的 PHP 进程长。 For high-traffic sites you should however consider moving rulesets into the vhost server configuration, once they've been battle-tested.但是,对于高流量站点,一旦经过实战测试,您应该考虑将规则集移动到虚拟主机服务器配置中。

    In this case, prefer the optionalized ^/?在这种情况下,更喜欢可选的^/? directory separator prefix.目录分隔符前缀。 This allows to move RewriteRules freely between PerDir and server config files.这允许在 PerDir 和服务器配置文件之间自由移动 RewriteRules。

  • Whenever something doesn't work每当某些事情不起作用

    Fret not.不要担心。

    • Compare access.log and error.log比较access.logerror.log

      Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log .通常你可以通过查看你的error.logaccess.log来弄清楚 RewriteRule 的行为异常。 Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).关联访问时间以查看最初进入的请求路径,以及 Apache 无法解析到的路径/文件(错误 404/500)。

      This doesn't tell you which RewriteRule is the culprit.这并没有告诉您哪个 RewriteRule 是罪魁祸首。 But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.但是像/docroot/21-.itle?index.php这样不可访问的最终路径可能会泄露进一步检查的位置。 Otherwise disable rules until you get some predictable paths.否则禁用规则,直到你得到一些可预测的路径。

    • Enable the RewriteLog启用重写日志

      See Apache RewriteLog docs.请参阅Apache RewriteLog文档。 For debugging you can enable it in the vhost sections:对于调试,您可以在虚拟主机部分启用它:

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

      That yields a detailed summary of how incoming request paths get modified by each rule:这产生了每个规则如何修改传入请求路径的详细摘要:

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

      Which helps to narrow down overly generic rules and regex mishaps.这有助于缩小过于通用的规则和正则表达式的失误。

      See also:也可以看看:
      · .htaccess not working (mod_rewrite) · .htaccess 不工作 (mod_rewrite)
      · Tips for debugging .htaccess rewrite rules · 调试.htaccess 重写规则的技巧

    • Before asking your own question在问自己的问题之前

      As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite.您可能知道,Stack Overflow 非常适合在 mod_rewrite 上提问。 Make them on-topic by including prior research and attempts (avoid redundant answers), demonstrate basic understanding, and:通过包括先前的研究和尝试(避免多余的答案)使它们成为主题,展示基本的理解,并且:

      • 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 和 PHPs $_SERVER环境(如果它与参数不匹配有关)。
      • An excerpt from your access.log and error.log to verify what the existing rules resolved to.您的access.logerror.log的摘录,用于验证现有规则解析的内容。 Better yet, a rewrite.log summary.更好的是,一个rewrite.log总结。

      This nets quicker and more exact answers, and makes them more useful to others.这可以更快,更准确地获得答案,并使它们对其他人更有用。

  • Comment your .htaccess评论您的.htaccess

    If you copy examples from somewhere, take care to include a # comment and origin link .如果您从某处复制示例,请注意包含# comment and origin link While it's merely bad manners to omit attribution, it often really hurts maintenance later.虽然忽略归因只是一种不礼貌的做法,但它通常确实会损害以后的维护。 Document any code or tutorial source.记录任何代码或教程源。 In particular while unversed you should be all the more interested in not treating them like magic blackboxes.特别是在不熟悉的情况下,您应该更感兴趣的是不要将它们视为魔法黑匣子。

  • It's not "SEO"-URLs这不是“SEO”-URL

    Disclaimer: Just a pet peeve.免责声明:只是一个小问题。 You often hear pretty URL rewriting schemes referred to as "SEO" links or something.您经常听到漂亮的 URL 重写方案被称为“SEO”链接或其他东西。 While this is useful for googling examples, it's a dated misnomer.虽然这对于搜索示例很有用,但它是一个过时的误称。

    None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter.现代搜索引擎都不会真正受到路径段中的.html.php?id=123查询字符串的干扰。 Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths.旧的搜索引擎,例如 AltaVista,确实避免了抓取具有潜在不明确访问路径的网站。 Modern crawlers are often even craving for deep web resources.现代爬虫通常甚至渴望深度网络资源。

    What "pretty" URLs should conceptionally be used for is making websites user-friendly .从概念上讲,“漂亮”的 URL 应该用于使网站对用户友好

    1. Having readable and obvious resource schemes.具有可读且明显的资源方案。
    2. Ensuring URLs are long-lived (AKA permalinks ).确保 URL 是长期存在的(AKA永久链接)。
    3. Providing discoverability through /common/tree/nesting .通过/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.大多数情况下只输出[^/]+通用占位符,但对于琐碎的站点可能就足够了。

Alternatives to mod_rewrite mod_rewrite 的替代品

Many basic virtual URL schemes can be achieved without using RewriteRules.许多基本的虚拟 URL 方案可以在不使用 RewriteRules 的情况下实现。 Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument. Apache 允许在没有.php扩展名的情况下调用 PHP 脚本,并使用虚拟PATH_INFO参数。

  1. Use the PATH_INFO , Luke使用PATH_INFO ,卢克

    Nowadays AcceptPathInfo On is often enabled by default.现在AcceptPathInfo On通常默认启用。 Which basically allows .php and other resource URLs to carry a virtual argument:这基本上允许.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.这不像让 Apache 将输入路径段分成$1$2$3并将它们作为不同的$_GET变量传递给 PHP 那样方便。 It's merely emulating "pretty URLs" with less configuration effort.它只是用更少的配置工作来模拟“漂亮的 URL”。

  2. Enable MultiViews to hide the .php extension启用MultiViews以隐藏.php扩展名

    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.由于匹配的基本名称,这使 Apache 为/article上的 HTTP 请求选择article.php And this works well together with the aforementioned PATH_INFO feature.这与前面提到的 PATH_INFO 功能配合得很好。 So you can just use URLs like http://example.com/article/virtual/title .因此,您可以只使用http://example.com/article/virtual/title之类的 URL。 Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.如果您有一个具有多个 PHP 调用点/脚本的传统 Web 应用程序,这很有意义。

    Note that MultiViews has a different/broader purpose though.请注意,MultiViews 具有不同/更广泛的用途。 It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames.它会导致非常小的性能损失,因为 Apache 总是寻找具有匹配基本名称的其他文件。 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 ).它实际上是用于Content-Negotiation ,因此浏览器会在可用资源(例如article.en.phparticle.fr.phparticle.jp.mp4 )中获得最佳选择。

  3. SetType or SetHandler for extensionless .php scripts用于无扩展名.php脚本的 SetType 或 SetHandler

    A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes.避免在 URL 中携带.php后缀的更直接的方法是为其他文件方案配置 PHP 处理程序 The simplest option is overriding the default MIME/handler type via .htaccess :最简单的选项是通过.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脚本重命名为仅article (不带扩展名),但仍将其作为 PHP 脚本处理。

    Now this can have some security and performance implications, because all extensionless files would be piped through PHP now.现在这可能会对安全性和性能产生一些影响,因为现在所有无扩展名文件都将通过 PHP 进行管道传输。 Therefore you can alternatively set this behaviour for individual files only:因此,您也可以只为单个文件设置此行为:

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

    This is somewhat dependent on your server setup and the used PHP SAPI.这在某种程度上取决于您的服务器设置和使用的 PHP SAPI。 Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script .常见的替代方案包括ForceType application/x-httpd-phpAddHandler php5-script

    Again take note that such settings propagate from one .htaccess to subfolders.再次注意,此类设置会从一个.htaccess传播到子文件夹。 You always should disable script execution ( SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.您始终应该禁用静态资源和上传/目录等的脚本执行( SetHandler NoneOptions -Execphp_flag engine off等)。

  4. Other Apache rewriting schemes其他 Apache 重写方案

    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrite s RewriteRules.在其众多选项中,Apache 提供了mod_alias功能——有时它的工作原理与mod_rewrite的 RewriteRules 一样好。 Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.请注意,其中大部分必须在<VirtualHost>部分中设置,而不是在每个目录的.htaccess配置文件中。

    • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. ScriptAliasMatch主要用于 CGI 脚本,但也应该适用于 PHP。 It allows regexps just like any RewriteRule .它允许正则表达式,就像任何RewriteRule一样。 In fact it's perhaps the most robust option to configurate a catch-all front controller.事实上,它可能是配置一个包罗万象的前端控制器的最强大的选择。

    • And a plainAlias 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.即使是简单的ErrorDocument指令也可以用来让 PHP 脚本处理虚拟路径。 Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.请注意,这是一个笨拙的解决方法,但是禁止 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

A frequent question about URL rewriting goes something like this:一个关于 URL 重写的常见问题是这样的:

I currently have URLs that look like this:我目前有如下所示的 URL:

I made them pretty like this:我把它们做成了这样:

By using this in my .htaccess file:通过在我的 .htaccess 文件中使用它:

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

But I want them to look like this:但我希望它们看起来像这样:

How can I change my .htaccess file to make that work?如何更改我的 .htaccess 文件以使其正常工作?


The simple answer is that you can't.简单的答案是你不能。

Rewrite rules don't make ugly URLs pretty, they make pretty URLs ugly重写规则不会使丑陋的 URL 变得漂亮,它们会使漂亮的 URL 变得丑陋

Whenever you type in a URL in a web browser, or follow a link, or display a page that references an image, etc, the browser makes a request for a particular URL.每当您在 Web 浏览器中输入 URL、点击链接或显示引用图像的页面等时,浏览器都会请求特定 URL。 That request ends up at a web server, and the web server gives a response .该请求最终到达 Web 服务器,Web 服务器给出响应

A rewrite rule is simply a rule that says "when the browser requests a URL that looks like X, give them the same response as if they'd requested Y".重写规则只是一条规则,它说“当浏览器请求一个看起来像 X 的 URL 时,给他们与他们请求 Y 相同的响应”。

When we make rules to handle "pretty URLs", the request is the pretty URL , and the response is based on the internal ugly URL .当我们制定规则来处理“漂亮的 URL”时,请求漂亮的 URL响应是基于内部丑陋的 URL It can't go the other way around, because we're writing the rule on the server, and all the server sees is the request the browser sent it.它不能反过来,因为我们正在服务器上编写规则,而服务器看到的只是浏览器发送的请求。

You can't use information that you don't have你不能使用你没有的信息

Given this basic model of what a rewrite rule does, imagine you were giving the instructions to a human.给定重写规则的基本模型,假设您正在向人类发出指令。 You could say:你可以说:

  • If you see a number in the request, like the "42" in "http://example.com/my-blog/42--i-found-the-answer", put that number on the end of "my-blog/entry.php?id="如果您在请求中看到一个数字,例如“http://example.com/my-blog/42--i-found-the-answer”中的“42”,请将该数字放在“my-博客/entry.php?id="

But if the information isn't there in the request, your instructions won't make any sense:但是,如果请求中没有信息,您的指示将没有任何意义:

  • If the request has "my-blog" in it, like "http://example.com/my-blog/i-found-the-answer", put the right number on the end of "my-blog/entry.php?id="如果请求中包含“my-blog”,例如“http://example.com/my-blog/i-found-the-answer”,请将正确的数字放在“my-blog/entry”的末尾。 php?id="

The person reading those instructions is going to say "Sorry, how do I know what the right number is?"阅读这些说明的人会说“对不起,我怎么知道正确的数字是多少?”

Redirects: "This URL is currently out of office..."重定向:“此 URL 当前不在办公室……”

Sometimes, you see rules that are the other way around , like this:有时,您会看到相反的规则,如下所示:

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

This rule does match an ugly URL on the left, and produce a pretty URL on the right.此规则匹配左侧的丑陋 URL,在右侧生成漂亮的 URL。 So surely we could write it without the ID at the beginning of the pretty part?所以我们肯定可以在漂亮部分的开头不使用 ID 来编写它吗?

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

The important difference is the [R] flag, which means that this rule is actually a redirect - instead of "serve the response from this URL", it means "tell the browser to load this URL instead".重要的区别是[R]标志,这意味着这个规则实际上是一个重定向——而不是“提供来自这个 URL 的响应”,它的意思是“告诉浏览器加载这个 URL”。

You can think of this like one of those automated e-mail replies, saying "Sorry, Joe Bloggs is currently on holiday; please send your message to Jane Smith instead."您可以将其想象为其中一封自动回复的电子邮件,内容为“抱歉,Joe Bloggs 目前正在度假;请将您的信息发送给 Jane Smith。” In the same way, the redirect above tells the browser "Sorry, there's no content for http://example.com/my-blog/entry.php?id=42 ; please request http://example.com/my-blog/42--i-found-the-answer instead.同样,上面的重定向告​​诉浏览器“对不起,没有http://example.com/my-blog/entry.php?id=42的内容;请请求http://example.com/my-blog/42--i-found-the-answer代替。

The important point of this analogy is that the above message wouldn't be much use if there wasn't actually anyone called Jane Smith working there, or if they had no idea how to answer the questions Joe Bloggs normally dealt with.这个类比的重点是,如果实际上没有任何叫 Jane Smith 的人在那里工作,或者如果他们不知道如何回答 Joe Bloggs 通常处理的问题,那么上面的信息就没有多大用处。 Similarly, a redirect is no use if the URL you tell the browser to request doesn't actually do anything useful.同样,如果您告诉浏览器请求的 URL 实际上没有做任何有用的事情,那么重定向也没有用。 Once the browser follows the redirect, it's going to make a new request , and when the server receives the new request, it still won't know what the ID number is.一旦浏览器跟随重定向,它就会发出一个新的请求,当服务器收到新的请求时,它仍然不知道 ID 号是什么。

But some sites do it, so it must be possible!但是有些网站会这样做,所以它一定是可能的!

A web server only has the information present in the request, but how it uses that information is up to you. Web 服务器只有请求中存在的信息,但它如何使用这些信息取决于您。

For instance, rather than looking up a blog post by ID, you could store its URL directly in the database, then write some code to do the matching directly in PHP, Python, node.js, etc. Or you could have the same URL show different content based on the language the user has set in their browser, or based on a cookie, etc.例如,您可以直接将其 URL 存储在数据库中,然后编写一些代码直接在 PHP、Python、node.js 等中进行匹配,而不是通过 ID 查找博客文章。或者您可以使用相同的 URL根据用户在浏览器中设置的语言或基于 cookie 等显示不同的内容。

Another thing you can do is use a form (or API request) with a method of POST rather than GET.您可以做的另一件事是使用带有 POST 而不是 GET 方法的表单(或 API 请求)。 That means additional information is sent in the "body" of the request, separate from the URL.这意味着附加信息在请求的“正文”中发送,与 URL 分开。 It still has to be sent , but it's not as obvious in the browser, won't be included in bookmarks, etc.它仍然必须发送,但在浏览器中不那么明显,不会包含在书签等中。

But you can't write a single line in a .htaccess file that performs miracles.但是你不能在 .htaccess 文件中写一行来创造奇迹。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM