HTTP重定向与内容协商
概念引入
想象你去飞翔公司找航仔(后端),前台告诉你:"航仔搬到18楼了,请去那边找他。"这就是重定向——你问的是A地址,被告知要去B地址。
再想象你去飞翔餐厅点餐,服务员问:"您要粤式口味还是川式口味?要大厅还是包间?"这就是内容协商——双方沟通,确定最合适的方案。
HTTP协议中的重定向和内容协商,就是这两种生活场景的数字化表达。今天,我们以飞翔公司官网多语言版本和架构升级的故事,彻底搞懂它们。
核心内容
重定向状态码
当资源位置发生变化,服务器无法直接响应时,会返回重定向状态码,并在Location头中告知新地址。
常见重定向状态码:
| 状态码 | 含义 | 方法是否改变 | SEO影响 | 飞翔公司场景 |
|---|---|---|---|---|
| 301 | 永久重定向 | 可能变为GET | 权重转移给新URL | 官网从http://升级到https:// |
| 302 | 临时重定向 | 可能变为GET | 权重保留在原URL | 活动页临时维护,跳转到公告页 |
| 307 | 临时重定向 | 严格保持原方法 | 权重保留在原URL | POST提交表单,临时换服务器处理 |
| 308 | 永久重定向 | 严格保持原方法 | 权重转移给新URL | API端点永久迁移,POST还是POST |
Location:HTTP响应头,用于重定向时指定资源的新URI。
// 301 永久重定向示例:飞翔官网强制HTTPS
GET http://www.feixiang.net/ HTTP/1.1
HTTP/1.1 301 Moved Permanently
Location: https://www.feixiang.net/
// 浏览器自动跟随Location发起新请求
GET https://www.feixiang.net/ HTTP/1.1
301 vs 302 对SEO的影响
鸣哥(内容运营)很关心这个问题:飞翔博客的旧文章链接被大量外部网站引用,如果换域名,流量会不会丢?
- 301 永久重定向:搜索引擎会把旧URL的"权重"(PageRank)大部分传递给新URL。适合域名变更、URL结构调整。
- 302 临时重定向:搜索引擎认为旧URL只是暂时不可用,权重保留在旧URL。适合临时维护、A/B测试。
// 飞翔博客迁移:旧域名 → 新域名,用301传递SEO权重
GET https://blog.feixiang.net/old-article HTTP/1.1
HTTP/1.1 301 Moved Permanently
Location: https://www.feixiang.net/blog/new-article
雁姐(用户运营)提醒:"302用多了会让搜索引擎困惑,该用301的时候别偷懒!"
307和308:方法保持的严谨重定向
302/301有个历史遗留问题:部分浏览器会把POST重定向变成GET请求,导致表单数据丢失。
307/308明确规定:重定向后的请求必须使用与原始请求相同的方法。
// 308 永久重定向:POST保持POST
POST https://api.feixiang.net/v1/login HTTP/1.1
Content-Type: application/json
{"username":"hangzai","password":"***"}
HTTP/1.1 308 Permanent Redirect
Location: https://api.feixiang.net/v2/login
// 浏览器自动用POST请求新地址,Body不变
POST https://api.feixiang.net/v2/login HTTP/1.1
Content-Type: application/json
{"username":"hangzai","password":"***"}
翼王(架构师)在API升级时统一使用308:"v1接口永久下线,全部导向v2,而且用户的POST请求不能变成GET!"
内容协商:找到最合适的资源版本
内容协商(Content Negotiation):客户端和服务器通过HTTP头部交换偏好信息,由服务器选择最合适的资源版本返回。
飞翔公司官网服务全球用户,需要支持中文、英文、日文等多语言。用户访问www.feixiang.net,服务器怎么知道该返回哪个语言版本?
客户端驱动的内容协商:浏览器通过请求头告诉服务器自己的偏好。
| 请求头 | 说明 | 飞翔公司示例 |
|---|---|---|
Accept | 可接受的MIME类型 | Accept: text/html, application/json |
Accept-Language | 偏好的语言 | Accept-Language: zh-CN, en-US;q=0.9 |
Accept-Encoding | 支持的压缩格式 | Accept-Encoding: gzip, deflate, br |
Accept-Charset | 支持的字符集 | Accept-Charset: utf-8 |
// 浏览器告诉服务器:我要中文,支持gzip压缩
GET / HTTP/1.1
Host: www.feixiang.net
Accept-Language: zh-CN, zh;q=0.9, en;q=0.8
Accept-Encoding: gzip, deflate
Accept: text/html
// 服务器返回中文页面,并用gzip压缩
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: zh-CN
Content-Encoding: gzip
服务端驱动 vs 客户端驱动 vs 透明协商
三种方式对比:
| 方式 | 决策方 | 优点 | 缺点 | 飞翔应用 |
|---|---|---|---|---|
| 客户端驱动 | 服务器根据请求头选择 | 标准、灵活 | 请求头可能过长 | 官网多语言版本 |
| 服务端驱动 | 服务器根据IP/UA判断 | 用户无感知 | 可能判断错误 | 早期飞翔官网自动跳转 |
| 透明协商 | 中间代理/CDN决策 | 减轻服务器负担 | 实现复杂 | 阿里云CDN智能调度 |
星宇(产品助理)建议:"飞翔官网用客户端驱动+语言切换按钮兜底。如果Accept-Language识别错了,用户还能手动切换。"
gzip压缩协商实战
凌叔(运维)发现飞翔官网的main.js有500KB,传输很慢。启用gzip压缩后降到150KB,速度提升3倍!
// 请求:浏览器声明支持gzip
GET /static/js/main.js HTTP/1.1
Host: www.feixiang.net
Accept-Encoding: gzip, deflate, br
// 响应:服务器返回gzip压缩内容
HTTP/1.1 200 OK
Content-Type: application/javascript
Content-Encoding: gzip
Content-Length: 153600
// 原始500KB → 压缩后150KB
风速(算法)补充:"Brotli(br)压缩比gzip更高,飞翔官网对支持br的浏览器优先返回br格式,能再省20%流量。"
飞翔公司官网多语言与压缩实战
// 中国用户访问飞翔官网
GET / HTTP/1.1
Host: www.feixiang.net
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: br,gzip
Accept: text/html
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: zh-CN
Content-Encoding: br
Vary: Accept-Encoding, Accept-Language
// 返回中文首页,Brotli压缩
Vary响应头:告诉缓存服务器,哪些请求头会影响响应内容。缓存必须根据这些头的不同值分别存储。
凌叔在Nginx配置了Vary: Accept-Encoding, Accept-Language,确保CDN不会把中文版缓存错发给美国用户。
重定向与内容协商结合场景
飞翔公司收购了一个海外域名feixiang.com,需要把流量引导到主站:
// 场景1:域名合并,301传递权重
GET https://feixiang.com/ HTTP/1.1
HTTP/1.1 301 Moved Permanently
Location: https://www.feixiang.net/
// 场景2:根据语言协商跳转到对应路径
GET https://www.feixiang.net/ HTTP/1.1
Accept-Language: en-US,en;q=0.9
HTTP/1.1 302 Found
Location: https://www.feixiang.net/en/
// 场景3:API版本升级,308保持POST方法
POST https://api.feixiang.net/v1/order HTTP/1.1
HTTP/1.1 308 Permanent Redirect
Location: https://api.feixiang.net/v2/order
本篇小结
- 重定向通过3xx状态码和
Location头将客户端引导到新地址 - 301(永久)传递SEO权重,适合域名/路径永久变更;302(临时)保留原URL权重,适合临时跳转
- 307/308严格保持原始请求方法,解决302/301可能将POST变为GET的问题
- 内容协商让客户端和服务器通过
Accept-*请求头和Content-*响应头协商最合适的资源版本 Accept-Language决定语言版本,Accept-Encoding决定压缩格式,Accept决定MIME类型- 飞翔官网通过
Vary: Accept-Encoding, Accept-Language确保CDN正确缓存多语言和多压缩版本 - 服务端驱动、客户端驱动、透明协商三种方式各有适用场景
动手实践
温馨提示: 以下实践示例中涉及的域名(如
www.feixiang.net)、公司场景和接口均为虚构数据,仅用于演示协议原理,实际执行时可能不会产生文档中描述的效果。建议将命令中的域名替换为你自己可访问的真实地址进行练习。
实践:用curl观察重定向
# 跟随重定向,查看完整跳转链
curl -L -I http://www.feixiang.net
# 不跟随重定向,只看第一个响应
curl -I http://www.feixiang.net
# 观察返回的是301还是302,以及Location头
实践:测试内容协商头部
# 请求英文版本
curl -H "Accept-Language: en-US" -I https://www.feixiang.net
# 请求中文版本
curl -H "Accept-Language: zh-CN" -I https://www.feixiang.net
# 观察Content-Language和可能的重定向Location
实践:测试gzip压缩协商
# 不带Accept-Encoding
curl -I https://www.feixiang.net/static/js/main.js
# 带gzip
curl -H "Accept-Encoding: gzip" -I https://www.feixiang.net/static/js/main.js
# 观察Content-Encoding和Content-Length的变化
实践:思考题
靓晴(UI设计)把飞翔官网的图片资源从
/images/迁移到了/static/images/。她使用了302临时重定向。三个月后,搜索引擎收录的还是旧路径,新路径没有权重。请分析原因,并给出正确的重定向方案。