重定向类型

一个看似简单的选择

重定向逻辑写完了,但我盯着代码里的一个数字发呆:301

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

我搜了半天,发现这个选择的影响比想象中大得多。301 还是 302,看起来只是改个数字,但会影响统计数据、用户体验,甚至 SEO。

HTTP 重定向状态码

HTTP 协议定义了多种重定向状态码:

状态码名称含义
301Moved Permanently永久重定向
302Found临时重定向
303See Other查看其他位置
307Temporary Redirect临时重定向(保持方法)
308Permanent Redirect永久重定向(保持方法)

最常用的是 301302。我需要理解它们的区别。

301 永久重定向

浏览器会缓存

HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/original-long-url
Cache-Control: max-age=31536000

301 表示资源已永久移动到新位置。浏览器会缓存这个重定向关系。

我做了个实验:

首次访问

用户访问 short.url/abc123

请求我的服务器

返回 301 + 目标 URL

浏览器跳转,并缓存这个关系

再次访问

用户访问 short.url/abc123

浏览器发现缓存中有这个短链接的重定向

直接跳转,不请求我的服务器!

好处与坏处

好处是快——用户第二次访问时,直接从浏览器缓存跳转,延迟为 0。

但坏处也很明显:我统计不到点击了

用户访问 100 次,我的服务器只收到第 1 次请求。剩下的 99 次都是浏览器直接跳转。我的点击统计完全失效了。

更大的问题:无法修改

还有一个更严重的问题:如果用户想修改短链接的目标 URL 呢?

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

如果我用 301,浏览器已经缓存了旧的重定向关系。用户修改了目标 URL,但访问短链接时还是跳到旧的地址。

用户访问 short.url/abc123

浏览器缓存命中

直接跳转到 blog.example.com/post-1(旧地址!)

修改无效!用户看不到新目标

301 的语义是”永久”,浏览器会认真对待这个承诺。

302 临时重定向

HTTP/1.1 302 Found
Location: https://www.example.com/original-long-url
Cache-Control: no-cache

302 表示资源临时从不同位置访问。浏览器不应缓存这个重定向关系。

每次访问

用户访问 short.url/abc123

请求我的服务器

返回 302 + 目标 URL

跳转(但不缓存)

下次访问时,流程一样,会再次请求我的服务器。

好处:完整的控制权

使用 302,每次访问都会经过我的服务器。这给了我完整的控制权:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

这些功能用 301 是无法实现的——浏览器缓存后根本不请求服务器。

坏处:多了一次往返

每次访问都要请求我的服务器,多了一次网络往返。性能上比 301 差一些。

指标301302
首次响应~50ms~50ms
再次访问0ms(缓存)~50ms
服务器压力

SEO 权重传递

研究了 SEO 之后,我才知道 301 和 302 还有一个重要区别:权重传递

301 会传递 SEO 权重

搜索引擎看到 301 时,会将原始 URL 的 SEO 权重传递给目标 URL。

short.url/abc123(权重:100)
    ↓ 301 重定向
example.com/target(获得权重:100)

这适用于网站迁移场景:旧域名 → 新域名,权重跟着转移。

302 不会传递权重

搜索引擎看到 302 时,认为这是临时的,权重保留在原始 URL。

short.url/abc123(权重:100)
    ↓ 302 重定向
example.com/target(权重:0)

short.url/abc123 仍然保留权重 100

我的短链接服务需要 SEO 吗?

对于短链接服务,权重应该留在哪里?

我思考了一下:

  1. 短链接域名本身不需要 SEO:用户是通过其他渠道(社交媒体、短信)看到短链接的,不是通过搜索引擎搜索”短链接”找到的。

  2. 目标 URL 应该获得权重:如果有人分享了短链接指向他的博客,权重应该传递给博客,而不是留在短链接域名。

从这个角度,似乎应该用 301?

但我又想到另一个问题:短链接服务本身不是内容生产者。搜索引擎爬虫访问短链接时,应该让它跳转到目标内容,而不是把权重留在短链接域名。

不过,大多数短链接的目标 URL 本身就有自己的 SEO,不需要通过短链接获得权重。而且,很多短链接指向的是临时内容、活动页面,这些页面本来就不需要长期 SEO。

权衡之下,我决定不把 SEO 作为主要考虑因素。

我的选择:302

综合考虑后,我选择了 302。原因如下:

原因 1:需要准确的点击统计

短链接服务的重要功能就是点击统计。用户创建短链接后,想知道有多少人点击了,从哪里点击的。

301 的问题

首次访问:请求服务器 → 301 重定向 → 浏览器缓存
再次访问:[不请求服务器] → 直接从缓存跳转 ❌ 统计丢失

302 的方案

每次访问:请求服务器 → 记录统计 → 302 重定向 ✅ 统计完整

原因 2:原始 URL 可能会变

用户可能需要修改短链接的目标 URL:

  • 商品链接换了
  • 博客迁移了
  • 活动页面更新了

用 302,我可以随时更新目标 URL,用户立即生效。用 301,浏览器缓存会导致修改无效。

原因 3:需要权限控制

短链接可能需要:

  • 禁用功能(违规链接)
  • 过期时间(临时链接)
  • 密码保护(私密链接)

这些都需要每次请求都经过服务器验证。301 的缓存机制无法实现。

方案落地

Flask 实现

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

Go 实现

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

添加缓存控制头

为了防止中间代理缓存 302 响应,我添加了明确的缓存控制头:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

这样即使有 CDN 或代理服务器,也不会缓存我的重定向响应。

本章小结

特性301302
语义永久重定向临时重定向
浏览器缓存✅ 会缓存❌ 不缓存
SEO 权重传递给目标保留在原始
服务器请求仅首次每次
短链接适用性❌ 不推荐✅ 推荐

短链接服务选择 302 的理由

  1. URL 可能需要修改
  2. 需要准确的点击统计
  3. 需要权限控制和状态检查

练习题

练习 1

如果用户反馈说修改了目标 URL 但短链接还是跳转到旧地址,可能是什么原因?如何解决?

参考答案

可能原因

  1. 使用了 301 重定向

    • 浏览器缓存了 301 重定向
    • 再次访问时直接从缓存跳转,不请求服务器
  2. CDN 缓存了重定向

    • CDN 节点缓存了 301 响应
    • 用户从 CDN 获取缓存的重定向
  3. 中间代理缓存

    • 公司/学校网络的代理服务器缓存了响应

解决方案

  1. 改用 302 重定向

    落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。
  1. 添加缓存控制头

    落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。
  1. 清除 CDN 缓存

    • 在 CDN 控制台刷新缓存
    • 配置 CDN 不缓存 302 响应
  2. 通知用户清除浏览器缓存

    • 临时方案:强制刷新(Ctrl+F5)
    • 或等待缓存自然过期

练习 2

如何在使用 302 的同时优化性能?

参考答案

优化策略

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

性能对比

方案平均响应时间数据库 QPS
直接查库~50ms10000
Redis 缓存~5ms100(99% 降低)

关键优化点

  1. Redis 缓存热点数据
  2. 点击统计异步处理
  3. 缓存 1 小时,平衡性能和灵活性

悬念:缓存能两全其美吗?

302 虽然好,但每次都要查询数据库。如果我的短链接服务每天有 1000 万次访问,数据库能承受吗?

我想到一个方案:用 Redis 缓存短链接映射。这样既保留了 302 的灵活性(可以修改、可以统计),又能获得接近 301 的性能。

但这又带来新问题:缓存和数据库之间如何保持一致?如果我修改了目标 URL,缓存什么时候更新?

下次我再深入探讨缓存方案。