301 vs 302 的抉择

大客户的”特殊要求”

Redis 缓存上线后,系统的响应时间稳定在 5ms 左右。我正准备庆祝,一个电商大客户找上门来。

“你们的短链接服务不错,“他们的技术负责人在电话里说,“但我们有个要求——必须使用 301 永久重定向。”

我愣了一下:“301?但我们现在用的是 302……”

“我们做 SEO 优化,需要把权重传递给目标页面。“他的语气很坚定,“如果用 302,搜索引擎不会传递权重,这对我们很重要。”

挂了电话,我陷入沉思。301 vs 302,这个看似简单的选择,背后是性能、SEO、统计准确性的复杂权衡。

在缓存环境下重新审视

之前我选择 302 的理由很简单:需要统计点击,需要灵活修改目标 URL。但现在有了 Redis 缓存,情况变得复杂了。

301 + 浏览器缓存

第一次访问:
客户端 → 短链接服务 → 301 → 目标 URL
浏览器缓存: short.url/abc → target.com (永久)

第二次访问:
客户端 → 浏览器缓存 → 目标 URL (不经过服务器)
速度: 0ms

优势:

  • 极快:浏览器缓存后,不再请求服务器
  • SEO 友好:搜索引擎会把权重传递给目标页面

劣势:

  • 统计缺失:无法统计缓存后的点击
  • 无法修改:即使修改数据库,浏览器仍然跳到旧地址

302 + Redis 缓存

每次访问:
客户端 → Nginx → Redis (5ms) → 302 → 目标 URL
同时: 异步写入统计

速度: ~5ms
统计: 准确记录每次点击

优势:

  • 准确统计:每次请求都经过服务器
  • 灵活可变:随时修改目标 URL

劣势:

  • 比 301 慢:每次都要访问服务器
  • SEO 不友好:权重不传递

性能对比表

我画了张对比表:

方案首次访问二次访问统计准确SEO 传递可修改
301 无缓存50ms50ms❌✅❌
301 浏览器缓存50ms0ms❌✅❌
302 无缓存50ms50ms✅❌✅
302 + Redis5ms5ms✅❌✅

关键发现: 302 + Redis 的性能已经足够好(5ms),虽然不如 301 的二次访问(0ms),但对于短链接场景,5ms 完全可以接受。

SEO 权重到底多重要?

为了搞清楚 SEO 的影响,我专门咨询了做 SEO 的朋友老张。

“短链接场景下,SEO 权重传递不是核心需求。“他看了我的架构图后说。

“为什么?”

“你想,用户在什么场景下会点击短链接?”他反问。

“社交媒体、邮件、广告……”

“对,这些都是外链。“他解释道,“搜索引擎对外链的权重传递本来就很谨慎。而且,短链接服务本身就是一个中转层,真正的价值在于统计和追踪,而不是 SEO。”

他给我展示了 Google 的官方文档:

Google 对重定向的态度:
- 301: 传递大部分权重(约 90-99%)
- 302: 不传递权重,但也不会惩罚

对于短链接:
- 搜索引擎知道这是跳转服务
- 不会因为使用 302 而惩罚
- 关键是目标页面本身的质量

“那是不是说,我用 302 也没问题?”

“对。除非你的客户是做内容网站的,需要通过社交媒体分享来积累 SEO 权重。但大多数情况下,短链接的价值在于数据,不在 SEO。”

电商客户的需求分析

我回到大客户的需求。他们做电商,分享链接主要在:

  • 社交媒体活动
  • KOL 推广
  • 邮件营销

“这些场景,SEO 权重传递真的重要吗?”我自问。

仔细一想:

  • 社交媒体链接: 大部分是 nofollow,本来就不传递权重
  • 广告链接: 也不传递权重
  • 邮件链接: 更不存在 SEO 问题

结论: 这个客户对 SEO 的需求,可能是误解。

我的最终决策

经过深思熟虑,我做了决定:坚持默认使用 302,但给大客户提供可选的 301 模式。

数据库改造

首先,在数据库中增加重定向类型字段:

落地思路

  • 在 urls 表上加一个重定向类型字段,默认值是 302,只有显式指定时才写入 301。
  • 字段只表达「这条链接要用哪种重定向」,不表达目标 URL 本身,两者分开存。
  • 迁移期先给存量行填默认值 302,再让应用开始读这个字段,避免新旧代码同时跑时读到空值。
  • 兜底是字段为空时按 302 处理:空值只会出现在迁移漏掉的行上,不该让这类行跳到 404。

配置驱动的重定向

修改重定向逻辑,支持可配置:

落地思路

  • 跳转逻辑不再写死状态码,而是读 urls 里的类型字段来决定返回 301 还是 302。
  • 字段为空或不在允许集合里就落到默认的 302,绝不因为一个异常值把用户导向别的语义。
  • 配置放在链接级别而不是全局级别:同一批用户里可以既有 301 链接也有 302 链接。
  • 兜底是类型字段被写成非法值时按 302 处理,并在日志里记一笔,便于事后发现配置被写坏。

用户 API

创建短链接时,允许指定重定向类型:

落地思路

  • 创建接口接受一个可选的重定向类型参数,用户不传就用默认的 302。
  • 参数只允许约定的取值,传了非法值直接拒绝创建并说明原因,不要静默降级成默认值。
  • 创建成功后把类型随短码一起返回,让用户清楚自己这条链接会以什么语义被访问。
  • 后续允许修改时类型也可以跟着改:从 301 改回 302 是允许的,反方向的代价要在文档里写清楚。

管理界面

给用户提供选择界面:

交互要点

  • 前端逻辑重点是展示任务状态、错误原因和下一步操作。

统计准确性的价值

302 的最大优势是统计准确性。这为后续的数据分析打下了基础。

点击追踪

每次重定向,我都能记录:

落地思路

  • 每次重定向都记一条点击:短码、来源 IP、访问时间、User-Agent,落到 click_logs。
  • 301 链接的统计天生不完整——浏览器缓存之后不再请求服务器,那部分点击根本到不了这里。
  • 统计写入走异步:先响应用户,再把点击事件投进消息队列,由消费者批量落库,避免拖慢跳转。
  • 兜底是队列消费失败时把事件重投,连续失败就落一条错误日志,不能因为统计把点击事件丢掉。

数据分析

有了这些数据,我能提供:

实时统计:
- 过去 1 小时点击量
- 过去 24 小时点击量
- 过去 7 天点击量

用户画像:
- 地理分布: 中国 60%, 美国 20%, 日本 10%
- 设备类型: 移动端 70%, 桌面端 30%
- 来源分析: 微信 40%, 微博 30%, 直接访问 20%

趋势分析:
- 点击高峰时段
- 周期性规律
- 异常流量检测

这些数据,是短链接服务的核心价值。

如果用 301,浏览器缓存后,这些数据都会丢失。

给客户的解释

我给电商客户发了一封邮件:

主题: 关于 301 重定向的建议

您好,

关于使用 301 永久重定向的需求,我做了详细的技术分析。

301 的优势是 SEO 权重传递,但有以下限制:
1. 浏览器会永久缓存,无法修改目标 URL
2. 无法统计缓存后的点击数据
3. 对社交媒体链接,SEO 效果有限(大部分是 nofollow)

对于您的电商场景,我建议:
- 营销活动链接: 使用 302,便于统计和修改
- 官方固定链接: 可以使用 301

我们支持两种模式,您可以根据需要选择。

附件是详细的技术对比文档。

Best,
短链接团队

客户很快回复了:“理解了,我们用 302。数据统计对我们更重要。”

性能与功能的平衡

最终,我的架构是这样的:

双层缓存策略

落地思路

  • 第一层是浏览器缓存:301 会被浏览器长期缓存,二次访问 0ms,但这部分点击服务端看不到。
  • 第二层是 Redis 缓存:302 每次都过服务端,但目标 URL 从 Redis 取,响应时间比直查数据库低一个量级。
  • 两层职责不同,不能互相替代:Redis 补的是服务端内部的查询耗时,浏览器缓存省掉的是整条网络往返。
  • 兜底是 Redis 缓存失效时回落直查数据库,响应变慢但跳转不中断。

性能监控

落地思路

  • 监控分三块看:301 与 302 的请求占比、Redis 缓存命中率、平均响应时间。
  • 占比用来确认客户实际选了什么类型,应该和监控面板里 301 占少数、302 占多数的预期对得上。
  • 命中率要单独盯:命中率掉下来先查缓存过期策略和键的数量,而不是直接加机器。
  • 兜底是给这三条指标都设告警线,出现异常时先降级保护跳转,再回头排查指标。

监控面板

实时监控:
├─ 总请求数: 10,234,567
├─ 301 重定向: 1,234,567 (12%)
├─ 302 重定向: 8,999,900 (88%)
└─ 平均响应时间: 4.2ms

缓存命中率:
├─ Redis 缓存: 98.5%
└─ 数据库查询: 1.5%

错误率: 0.01%

本章小结

301 vs 302 的权衡:

维度301302决策
SEO 权重✅ 传递❌ 不传递短链接场景不重要
浏览器缓存✅ 永久❌ 不缓存302 用 Redis 补偿
统计准确性❌ 无法统计✅ 准确核心价值
灵活性❌ 不可修改✅ 可修改运营必需
响应速度0ms (缓存后)5ms (Redis)都足够快

最终方案:

  • 默认 302: 满足统计需求和灵活性
  • 支持 301: 作为高级功能,给有 SEO 需求的客户
  • Redis 缓存: 优化 302 的性能,达到可接受的响应时间
  • 用户选择: 提供清晰的文档,让用户根据场景选择

核心认知:

  • 短链接服务的价值在于数据,不在 SEO
  • 性能优化可以用缓存解决,不必牺牲功能
  • 给用户选择权,而不是替用户做决定

练习题

练习 1

如果你的短链接服务主要服务于社交媒体分享(用户分享文章、视频等),应该选择 301 还是 302? 为什么?

重定向策略

解题思路:

选 301 还是 302,本质上取决于业务是否需要”可修改”和”可统计”。社交媒体分享这两个需求都占:文章可能被删改、点击数据是核心指标。所以要把 301 的缓存语义讲清楚,才能说明为什么它在这里不合适。

详细步骤:

推荐:302 + Redis 缓存

分析:

社交媒体分享的特点:

  1. 链接可能失效: 文章可能被删除、修改 URL 结构
  2. 需要统计: 分享次数、点击来源是重要指标
  3. 可能需要修改: 用户可能想修改分享的目标

301 的问题:

场景: 用户分享了一篇博客文章
- 使用 301 重定向到 blog.example.com/article-1
- 后来文章 URL 改为 blog.example.com/article-1-updated
- 由于浏览器缓存了 301,用户仍然跳到旧 URL
- 用户体验差,且无法统计

302 的优势:

- 可以随时修改目标 URL
- 准确统计每次点击
- 通过 Redis 缓存,性能不输 301
- SEO 权重虽然不是最优,但社交媒体本身权重高,影响有限

权衡结论: 对于社交媒体分享,灵活性和统计准确性比 SEO 权重传递更重要。

最终答案:

社交媒体分享应选 302 加 Redis 缓存。社交媒体分享有三个特点:链接可能失效(文章被删或改 URL 结构)、需要统计点击次数与来源、可能需要修改分享目标。301 的问题是浏览器会把”短码到目标地址”长期缓存下来,目标 URL 改了之后用户仍跳到旧地址,且点击无法统计。302 的优势是目标 URL 可随时修改、每次点击都能准确统计,且通过 Redis 缓存后性能并不输 301,SEO 权重传递上的损失在社交媒体场景本身影响有限。结论是灵活性和统计准确性比 SEO 权重传递更重要。

练习 2

设计一个 API,让用户可以选择重定向类型,并说明每种类型的适用场景。

短链接服务

解题思路:

接口设计的要点不在字段本身,而在两件事:默认值怎么定、非法输入怎么处理。默认值要能覆盖大多数场景,非法值必须显式报错而不是悄悄改掉,否则调用方永远发现不了自己传错了。

详细步骤:

API 设计:

落地思路

  • 接口接收长 URL 和可选的重定向类型,类型取值限定在 temporary、permanent、smart 三者之内。
  • 不传类型时按 smart 处理:由服务端根据用途决定,大多数分享类链接最终落到 temporary。
  • 返回体里要带上最终生效的类型,让调用方知道自己会拿到 301 还是 302。
  • 兜底是类型传了非法值就返回 400 并说明允许的取值,不要默默改成默认值。

使用示例:

验证要点

  • 用创建接口拿到返回的 short_code 和 redirect_type,确认前者是短码、后者是最终生效的重定向类型。
  • 再拿返回的短码发一次请求,检查响应状态码是 302、响应头的 Location 指向目标长 URL。
  • 响应时间这一项主要看是否命中 Redis 缓存:热门短链接近缓存耗时,冷门短链要回源,会明显慢一截。
  • 请求验证关注返回状态、跳转目标和响应时间。

适用场景总结:

场景推荐类型理由
社交媒体分享temporary (302)需要统计,可能修改
营销活动temporary (302)活动结束后可能修改
官方固定链接permanent (301)SEO 优化,不会修改
A/B 测试temporary (302)需要灵活切换
域名迁移permanent (301)永久迁移,传递权重
不确定smart让系统智能选择

最终答案:

接口接收长 URL 和可选的重定向类型,类型取值限定在 temporary、permanent、smart 三者之内;不传类型时按 smart 处理,由服务端根据用途决定,大多数分享类链接最终落到 temporary;返回体里要带上最终生效的类型,让调用方知道自己会拿到 301 还是 302;类型传了非法值就返回 400 并说明允许的取值,不要默默改成默认值。适用场景上,社交媒体分享、营销活动、A/B 测试用 temporary,因为需要统计或可能修改;官方固定链接、域名迁移用 permanent,因为不会改且要传递 SEO 权重;拿不准时用 smart 让系统智能选择。

流量洪峰的隐患

性能优化完成了,301 vs 302 的权衡也解决了。系统运行得越来越稳定,用户增长迅速。

直到有一天,一个热门短链接在社交媒体上爆了。

“某明星在微博发了一条短链接,瞬间带来了数万请求。“值班同事在群里喊道,“服务器负载飙升到 80%!”

我看着监控面板,心跳加速。虽然 Redis 缓存扛住了大部分请求,但单一热门链接的瞬间流量,仍然可能压垮系统。

单个短链接的流量集中,比分散的流量更可怕。

我需要更激进的优化策略……

发现内容有问题?

告诉我们哪里需要改进。