Location 就一个作用:告诉客户端「东西在另一个 URI 上」。
但它配 3xx 时是「去这里」,配 201 时是「新东西在这里」,同一个首部两种语义,只能靠状态码分。
前面 6.3.x 讲通用首部,6.4.x 讲请求首部,这里开始是真正「服务端说话」的字段。 第一个就是重定向的核心。
一、配 3xx:跟我走
301/302/303/307 都靠它指路。
301 是永久,302 是临时,303 强制换 GET,307 强制保持原方法——四个状态码的差别在 4.3 那几节讲过,
这里只看首部本身。
Demo 里 /old 回 302 加 Location: /new,客户端补上主机名再发一次,第一次 81 字节、
第二次 57 字节,正文只有最后那次算数,第一次那 5 字节 moved 被直接丢掉了。
这就是重定向的真实代价:多一个往返。移动端弱网下,一次多余的往返可能就是几百毫秒。
所以能配服务器直接跳就别用页面跳。
二、配 201:新东西在这儿
201 Created 表示「刚建好了一个资源」,Location 给的是这个新资源的地址。
客户端不会自动跟过去——它只是知道以后该去哪儿找。
Demo 里 POST 到 /item 回 201 加 Location: /item/7,响应就此结束,没有任何后续请求。
这是 REST 接口的标准做法:服务器分配了 ID,得告诉调用方。
容易搞混的是:同样是 Location,配 3xx 时客户端会跟,配 201 时不会。
判断依据只有状态码,不能看首部。
三、相对还是绝对
Location: /new 是相对路径,客户端按 RFC 3986 的规则用当前 URI 解析,补上协议和主机名。
Location: http://example.com/new 是绝对地址,客户端要丢掉原来的主机重连——跨域跳转就是这么来的。
规范允许两种写法,但实践中倾向于绝对地址:有些老客户端对相对路径处理不一致,
而且绝对地址能明确表达「去另一台机器」。
安全上要注意:跳转目标是用户可控的时候,必须做白名单校验。
否则一个 Location: https://evil.example/,就能把刚登录完的用户连人带 cookie 送出去。
四、动手 Demo:三种入口,三种 Location
一个服务端同时扮演三个角色:/old 是临时搬家、/abs 是搬到别的主机、/item 是刚创建完的新资源。
看同一个首部在这三种场景下的不同含义。
def fn(raw, st):
path = raw.split(b' ')[1].decode('latin-1')
if path == u'/old':
return X.reply(u'HTTP/1.1 302 Found', [(u'Location', u'/new')], b'moved')
if path == u'/abs':
return X.reply(u'HTTP/1.1 302 Found',
[(u'Location', u'http://example.com/new')], b'moved')
if path == u'/new':
return X.reply(u'HTTP/1.1 200 OK', [], b'')
return X.reply(u'HTTP/1.1 201 Created', [(u'Location', u'/item/7')], b'created')
实跑输出:
--------------------------------------------------------------------
Location:让浏览器去别处
--------------------------------------------------------------------
请求 /old → HTTP/1.1 302 Found
Location: /new
请求 /abs → HTTP/1.1 302 Found
Location: http://example.com/new
请求 /item → HTTP/1.1 201 Created
Location: /item/7
只有相对路径是能自动跟的:/old 回了 /new,客户端补上主机名再发一次。
第 1 次 81 字节 → HTTP/1.1 302 Found
第 2 次 57 字节 → HTTP/1.1 200 OK
合计两次往返,正文只有最后那次算数(moved 那 5 字节被丢掉了)
`/abs` 指向另一台主机,客户端要丢掉原主机名重连——跨域跳转就是这么来的。
`201 Created` 的 Location 语义不同:它不是「去这里」,是「新东西在这里」,
客户端不会自动跟过去。同一个首部,两个用途,只能靠状态码分。
/old 回 302 加相对路径 /new,客户端补主机名再发一次:第一次 81 字节、第二次 57 字节。
/abs 回的是完整地址,指向另一台主机。客户端必须丢掉原主机重连,这就是跨域跳转。
/item 回 201 加 /item/7,
注意客户端没有跟着发第二次请求——201 的 Location 只是「记录一下」,不是「现在就去」。
同一个首部,两种语义,只靠状态码区分。写客户端时这一点必须分清。
30 秒实验
- 把
/old的状态码改成 301,看客户端行为有没有区别。 - 把相对路径改成
../new,看客户端怎么解析。 - 想一想:为什么登录成功后跳转,最忌讳用用户传入的 URL 当 Location?
常见误区
- 「Location 一定表示跳转」——配 201 时不是,它只是新资源的地址。
- 「相对路径不能用」——能用,规范允许,客户端按当前 URI 解析。
- 「跳转是免费的」——每次多一个往返,实测 81 加 57 字节两次。
自测三问(点开对答案)
1. `201 Created` 里的 Location 是什么含义? —— 新建资源的 URI,客户端不会自动请求它。 2. `Location: /new` 是相对路径还是绝对?客户端怎么处理? —— 相对路径,按 RFC 3986 用当前 URI 补全协议和主机名。 3. 为什么开放重定向是漏洞? —— 攻击者能把跳转目标改成自己的站点,把用户和凭据一起带走。跳转是给人看的。接下来几个字段是给「中间那台机器」看的——先是代理的认证,再是「等会儿再来」。
- ☐ (本节完,下一节 6.5.5 Proxy-Authenticate)