aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/http/redirections/index.html
blob: a009739d505d588844c64c648483b500f4b6b2e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
---
title: HTTP 的重定向
slug: Web/HTTP/Redirections
tags:
  - 指南
  - 重定向
translation_of: Web/HTTP/Redirections
---
<div>{{HTTPSidebar}}</div>

<p class="summary">URL 重定向,也称为 URL 转发,是一种当实际资源,如单个页面、表单或者整个 Web 应用被迁移到新的 URL 下的时候,保持(原有)链接可用的技术。HTTP 协议提供了一种特殊形式的响应—— HTTP 重定向(HTTP redirects)来执行此类操作。</p>

<p>重定向可实现许多目标:</p>

<ul>
 <li>站点维护或停机期间的临时重定向。</li>
 <li>永久重定向将在更改站点的URL,上传文件时的进度页等之后保留现有的链接/书签。</li>
 <li>上传文件时的表示进度的页面。</li>
</ul>

<h2 id="原理">原理</h2>

<p>在 HTTP 协议中,重定向操作由服务器通过发送特殊的响应(即 redirects)而触发。HTTP 协议的重定向响应的状态码为 3xx 。</p>

<p>浏览器在接收到重定向响应的时候,会采用该响应提供的新的 URL ,并立即进行加载;大多数情况下,除了会有一小部分性能损失之外,重定向操作对于用户来说是不可见的。<br>
  </p>

<p><img alt="" src="https://mdn.mozillademos.org/files/13785/HTTPRedirect.png"></p>

<p>不同类型的重定向映射可以划分为三个类别:</p>

<ol>
 <li><a href="/zh-CN/docs/Web/HTTP/Redirections$edit#Permanent_redirections">永久重定向</a></li>
 <li><a href="/zh-CN/docs/Web/HTTP/Redirections$edit#Temporary_redirections">临时重定向</a></li>
 <li><a href="/zh-CN/docs/Web/HTTP/Redirections$edit#Special_redirections">特殊重定向</a></li>
</ol>

<h3 id="永久重定向">永久重定向</h3>

<p>这种重定向操作是永久性的。它表示原 URL 不应再被使用,而应该优先选用新的 URL。搜索引擎机器人会在遇到该状态码时触发更新操作,在其索引库中修改与该资源相关的 URL 。</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">编码</th>
   <th scope="col">含义</th>
   <th scope="col">处理方法</th>
   <th scope="col">典型应用场景</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>301</code></td>
   <td>Moved Permanently</td>
   <td>{{HTTPMethod("GET")}} 方法不会发生变更,其他方法有可能会变更为 {{HTTPMethod("GET")}} 方法。<sup><a href="#attr1">[1]</a></sup></td>
   <td>网站重构。</td>
  </tr>
  <tr>
   <td><code>308</code></td>
   <td>Permanent Redirect</td>
   <td>方法和消息主体都不发生变化。</td>
   <td>
    <p>网站重构,用于非GET方法。(with non-GET links/operations)</p>
   </td>
  </tr>
 </tbody>
</table>

<p><a id="attr1" name="attr1"></a>[1] 该规范无意使方法发生改变,但在实际应用中用户代理会这么做。 308 状态码被创建用来消除在使用非 GET 方法时的歧义行为。</p>

<h3 id="临时重定向">临时重定向</h3>

<p>有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问。在这种情况下可以使用临时重定向。</p>

<p>搜索引擎不会记录该新的、临时的链接。在创建、更新或者删除资源的时候,临时重定向也可以用于显示临时性的进度页面。</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">编码</th>
   <th scope="col">含义</th>
   <th scope="col">处理方法</th>
   <th scope="col">典型应用场景</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>302</code></td>
   <td><code>Found</code></td>
   <td>{{HTTPMethod("GET")}} 方法不会发生变更,其他方法有可能会变更为 {{HTTPMethod("GET")}} 方法。<sup><a href="#attr2">[2]</a></sup></td>
   <td>由于不可预见的原因该页面暂不可用。在这种情况下,搜索引擎不会更新它们的链接。</td>
  </tr>
  <tr>
   <td><code>303</code></td>
   <td><code>See Other</code></td>
   <td>{{HTTPMethod("GET")}} 方法不会发生变更,其他方法会<strong>变更</strong>为 GET 方法(消息主体会丢失)。</td>
   <td>用于{{HTTPMethod("PUT")}}{{HTTPMethod("POST")}} 请求完成之后进行页面跳转来防止由于页面刷新导致的操作的重复触发。</td>
  </tr>
  <tr>
   <td><code>307</code></td>
   <td><code>Temporary Redirect</code></td>
   <td>方法和消息主体都不发生变化。</td>
   <td>由于不可预见的原因该页面暂不可用。在这种情况下,搜索引擎不会更新它们的链接。当站点支持非 GET 方法的链接或操作的时候,该状态码优于 302 状态码。</td>
  </tr>
 </tbody>
</table>

<p><a id="attr2" name="attr2"></a>[2] 该规范无意使方法发生改变,但在实际应用中用户代理会这么做。 307 状态码被创建用来消除在使用非 GET 方法时的歧义行为。</p>

<h3 id="特殊重定向">特殊重定向</h3>

<p>除了上述两种常见的重定向之外,还有两种特殊的重定向。{{HTTPStatus("304")}} (Not Modified,资源未被修改)会使页面跳转到本地陈旧的缓存版本当中(该缓存已过期(?)),而 {{HTTPStatus("300")}} (Multiple Choice,多项选择) 则是一种手工重定向:以 Web 页面形式呈现在浏览器中的消息主体包含了一个可能的重定向链接的列表,用户可以从中进行选择。</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">编码</th>
   <th scope="col">含义</th>
   <th scope="col">典型应用场景</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>300</code></td>
   <td><code>Multiple Choice</code></td>
   <td>不常用:所有的选项在消息主体的 HTML 页面中列出。鼓励在 {{HTTPHeader("Link")}}  头部加入机器可读的 <code>rel=alternate</code></td>
  </tr>
  <tr>
   <td><code>304</code></td>
   <td><code>Not Modified</code></td>
   <td>发送用于重新验证的条件请求。表示缓存的响应仍然是新鲜的并且可以使用。</td>
  </tr>
 </tbody>
</table>

<h2 id="设定重定向映射的其他方法">设定重定向映射的其他方法</h2>

<p>HTTP 协议中重定向机制并非唯一的重定向映射的方式。其他两种方法包括:</p>

<ol>
 <li>借助 HTML 的 meta 元素的 HTML 重定向机制</li>
 <li>借助 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">DOM</a> 的 JavaScript 重定向机制。</li>
</ol>

<h3 id="HTML_重定向机制">HTML 重定向机制</h3>

<p>HTTP 协议中重定向机制是应该优先采用的创建重定向映射的方式,但是有时候 Web 开发者对于服务器没有控制权,或者无法对其进行配置。针对这些特定的应用情景,Web 开发者可以在精心制作的 HTML 页面的 {{HTMLElement("head")}}  部分添加一个 {{HTMLElement("meta")}} 元素,并将其 {{htmlattrxref("http-equiv", "meta")}} 属性的值设置为 <code>refresh</code> 。当显示页面的时候,浏览器会检测该元素,然后跳转到指定的页面。</p>

<pre class="brush: html notranslate">&lt;head&gt;
  &lt;meta http-equiv="Refresh" content="0; URL=http://example.com/" /&gt;
&lt;/head&gt;
</pre>

<p>{{htmlattrxref("content")}} 属性的值开头是一个数字,指示浏览器在等待该数字表示的秒数之后再进行跳转。建议始终将其设置为 0 来获取更好的可访问性。</p>

<p>显然,该方法仅适用于 HTML 页面(或类似的页面),然而并不能应用于图片或者其他类型的内容。</p>

<div class="note">
<p>注意这种机制会使浏览器的回退按钮失效:可以返回含有这个头部的页面,但是又会立即跳转。</p>
</div>

<h3 id="JavaScript_重定向机制">JavaScript 重定向机制</h3>

<p>在 JavaScript 中,重定向机制的原理是设置 {{domxref("window.location")}} 的属性值,然后加载新的页面。</p>

<pre class="brush: js notranslate">window.location = "http://example.com/";</pre>

<p>与 HTML 重定向机制类似,这种方式并不适用于所有类型的资源,并且显然只有在支持 JavaScript 的客户端上才能使用。另外一方面,它也提供了更多的可能性,比如在只有满足了特定的条件的情况下才可以触发重定向机制的场景。</p>

<h3 id="优先级">优先级</h3>

<p>由于存在上述三种 URL 重定向机制,那么在多种方法同时设定的情况下,哪种方法会首先起作用呢?优先级顺序如下:</p>

<ol>
 <li>HTTP 协议的重定向机制永远最先触发,即便是在没有传送任何页面——也就没有页面被(客户端)读取——的情况下。</li>
 <li>HTML 的重定向机制 ({{HTMLElement("meta")}}) 会在 HTTP 协议重定向机制未设置的情况下触发。</li>
 <li>JavaScript 的重定向机制总是作为最后诉诸的手段,并且只有在客户端开启了 JavaScript 的情况下才起作用。</li>
</ol>

<p>任何情况下,只要有可能,就应该采用 HTTP 协议的重定向机制,而不要使用  {{HTMLElement("meta")}} 标签。假如开发人员修改了 HTTP 重定向映射而忘记修改 HTML 页面的重定向映射,那么二者就会不一致,最终结果或者出现无限循环,或者导致其他噩梦的发生。</p>

<h2 id="应用场景">应用场景</h2>

<p>有以下几种应用场景可以使用重定向机制,但是需要注意应该尽可能地限制其使用数量,因为每一次重定向都会带来性能上的开销。</p>

<h3 id="域名别称">域名别称</h3>

<p>理想情况下,一项资源只有一个访问位置,也就是只有一个 URL 。但是由于种种原因,需要为资源设定不同的名称(即不同的域名,例如带有和不带有 www 前缀的URL,以及简短易记的 URL 等)。在这种情况下,实用的方法是将其重定向到那个实际的(标准的)URL,而不是复制资源。</p>

<p>在以下几种情况下可以使用域名别称:</p>

<dl>
 <dt>扩大站点的用户覆盖面。</dt>
 <dd>一个常见的场景是,假如站点位于  <code>www.example.com</code>  域名下,那么通过  <code>example.com </code>也应该可以访问到。这种情况下,可以建立从 <code>example.com</code> 的页面到  <code>www.example.com</code> 的重定向映射。此外还可以提供常见的同义词,或者该域名容易导致的拼写错误的域名别称。</dd>
 <dt>迁移到另外一个域名。</dt>
 <dd>例如,公司改名后,你希望用户在搜索旧名称的时候,依然可以访问到应用了新名称的站点。</dd>
 <dt>强制使用 HTTPS 协议。</dt>
 <dd>对于 HTTP 版本站点的请求会被重定向至采用了 HTTPS 协议的版本。</dd>
 <dt>
 <h3 id="保持链接有效">保持链接有效</h3>
 </dt>
</dl>

<p>当你重构 Web 站点的时候,资源的 URL 会发生改变。即便是你可以更新站点内部的链接来适应新的命名体系,但无法控制被外部资源使用的 URL 。</p>

<p>你并不想因此而使旧链接失效,因为它们会为你带来宝贵的用户(并且帮助优化你的SEO),所以需要建立从旧链接到新链接的重定向映射。</p>

<div class="note">
<p>即便是这项技术可以同样应用于内部链接,但是应该尽量避免内部重定向映射。重定向机制会带来相当大的性能开销(额外的 HTTP 请求),所以如果你可以通过修复链接来避免的话,那么就应该将其修复。</p>
</div>

<h3 id="对于不安全请求的临时响应">对于不安全请求的临时响应</h3>

<p>不安全({{Glossary("safe", "Unsafe")}})请求会修改服务器端的状态,应该避免用户无意的重复操作。</p>

<p>通常,你并不想要你的用户重复发送  {{HTTPMethod("PUT")}}{{HTTPMethod("POST")}} 或 {{HTTPMethod("DELETE")}} 请求。假如你仅仅为该类请求返回响应的话,简单地点击刷新按钮就会(可能会有一个确认信息)导致请求的重复发送。</p>

<p>在这种情况下,服务器可以返回一个 {{HTTPStatus("303")}} (See Other) 响应,其中含有合适的响应信息。如果刷新按钮被点击的话,只会导致该页面被刷新,而不会重复提交不安全的请求。</p>

<h3 id="对于耗时请求的临时响应">对于耗时请求的临时响应</h3>

<p>一些请求的处理会需要比较长的时间,比如有时候 {{HTTPHeader("DELETE")}} 请求会被安排为稍后处理。在这种情况下,会返回一个 {{HTTPStatus("303")}} (See Other)  重定向响应,该响应链接到一个页面,表示请求的操作已经被列入计划,并且最终会通知用户操作的进展情况,或者允许用户将其取消。</p>

<h2 id="在通用服务器中配置重定向">在通用服务器中配置重定向</h2>

<h3 id="Apache">Apache</h3>

<p>重定向映射可以在服务器的配置文件中设置,也可以在每一个文件目录的 .htaccess 文件中设置。</p>

<p><a href="https://httpd.apache.org/docs/current/mod/mod_alias.html">mod_alias</a> 模块提供了 <code>Redirect</code> 和 <code>Redirect_Match</code> 两种指令来设置 {{HTTPStatus("302")}} 响应(默认值):</p>

<pre class="notranslate">&lt;VirtualHost *:443&gt;
	ServerName example.com
	Redirect / https://www.example.com
&lt;/VirtualHost&gt;
</pre>

<p>URL <code>https://example.com/</code> 会被重定向至 <code>https://www.example.com/</code> ,URL 下的任何文件或目录也将重定向到该 URL(<code>https://example.com/some-page</code> 将重定向至 <code>https://www.example.com/some-page</code>)。</p>

<p><code>Redirect_Match</code> 指令的功能与之类似,不同之处在于它可以通过<a href="/zh-CN/docs/Glossary/Regular_expression">正则表达式</a>来指定一批受影响的 URL :</p>

<pre class="notranslate">RedirectMatch ^/images/(.*)$ http://images.example.com/$1</pre>

<p>位于 <code>images/</code> 文件夹下的所有文档都会被重定向至新的域名。</p>

<p>如果你不想要设置临时跳转,那么可是使用额外的参数(使用 HTTP 状态码或者 permanent 关键字)来进行设置:</p>

<pre class="notranslate">Redirect permanent / https://www.example.com
# …acts the same as:
Redirect 301 / https://www.example.com</pre>

<p><a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html">mod_rewrite</a> 模块也可以用来设置重定向映射。它应用起来更灵活,但也更加复杂。</p>

<h3 id="Nginx">Nginx</h3>

<p>在 Nginx 中,你可以创建一个服务器模块来进行重定向设置:</p>

<pre class="notranslate">server {
	listen 80;
	server_name example.com;
	return 301 $scheme://www.example.com$request_uri;
}</pre>

<p>可以使用 rewrite 指令来针对一个文件目录或者一部分页面应用重定向设置:</p>

<pre class="notranslate">rewrite ^/images/(.*)$ http://images.example.com/$1 redirect;
rewrite ^/images/(.*)$ http://images.example.com/$1 permanent;
</pre>

<h3 id="IIS">IIS</h3>

<p>在 IIS 中,你可以使用 <code><a href="https://www.iis.net/configreference/system.webserver/httpredirect">&lt;httpRedirect&gt;</a></code> 元素来配置重定向映射。</p>

<h2 id="重定向死锁(循环)">重定向死锁(循环)</h2>

<p>当后续的重定向路径重复之前的路径的时候,重定向循环就产生了。换句话说,就是陷入了无限循环当中,不会有一个最终的页面返回。</p>

<p>大多数情况下,这属于服务器端错误。如果服务器检测不到,就会返回 {{HTTPStatus("500")}} <code>Internal Server Error</code> 。假如你在修改了服务器配置不久就出现了这个问题,八成是遇到了重定向循环。</p>

<p>有时候,服务器端无法对其进行检测:重定向循环发生于多台服务器之间,对于每一台服务器来说,都无法获得一个全景图。在这种情况下,浏览器会负责进行检测,然后返回错误信息。Firefox 会呈现如下信息:</p>

<pre class="bz_comment_text notranslate" id="comment_text_0">Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
Firefox 检测到服务器正在试图将请求进行重定向,而这种重定向永远不会完结。
</pre>

<p>而 Chrome 则会呈现如下信息:</p>

<pre class="notranslate">This Webpage has a redirect loop
本页面包含有重定向循环。
</pre>

<p>无论哪个场景,用户对此都无能为力(除非客户端发生突变,比如说缓存或者Cookie不匹配)</p>

<p>避免重定向循环非常重要,因为它会完全毁掉用户的体验。</p>