短链接 · 为什么会存在 · 把长地址压成人能用的代号
你随手复制的一条链接,常常是一两百个字符,后面拖着一长串给机器看的 utm_* / ref tracking 参数。它机器能用,人却几乎用不了——没法念给人手敲、塞不进有字数上限的短信、印成二维码后码点过密难以扫描、贴进聊天框还折成三行。
短链就是来解决这个「长度 / 可用性」问题的:把任意长的 URL 换成一个极短、固定长度的代号 vega.link/aB3xK,真正的长地址存在服务端一张表里,访问短码时再 301/302 跳过去——这正是「为什么要有路由这一层」讲的那层 indirection
最直接的实例。它同时附带获得了 indirection 的全部好处(改址 / 统计 / 灰度),但它首先解决的,是「长度」这个最朴素的问题。
短链的本质,就一句话:一张映射表()+ 一次 301/302 跳转。短码极短(几个字符),所以它能念、能记、能印、能扫;真实目标藏在服务端,所以你随时能改它、能在跳转那一下记统计、能按渠道灰度——后面这一串好处和「为什么要有路由这一层」讲的完全是同一招,短链只是它最常见的实例。
1 · 为什么短码能这么短 · 自增 id 到 base62
短码怎么生成才能又短又不撞?最经典的做法:每来一个新链接,数据库给它一个自增 id(1, 2, 3…),再把这个十进制 id 用 base62(0-9 a-z A-Z 共 62 个字符)编码。base62
比十进制「进制大」,同样的数用更少的字符就能写完——而且自增天然不重复,不像随机短码要查重、也不像 hash 截断会撞。拖动下面的 id 看短码怎么变:
2 · 自增 id 的风险 · 别人能顺着猜到其他链接
base62 只是换了进制,是可逆的双射——拿到一条短码解回 id,
就是相邻的另一条短链。于是只要从 1 一路加上去,就能把整库的目标 URL 全爬下来(enumeration / IDOR);即便短码本身不可逆,单调递增也会泄露规模与增速——看到 id 到了 5000
万就知道你发了多少、采样两次还能算增长,这是经典的「德国坦克问题」。真实事故:goo.gl / 早期 bit.ly 都被枚举过,暴露出大量本以为「不公开」的网盘分享。要害是一句话:短码的「难猜」当不了访问控制,它只负责寻址。
上面右列用一个可逆置换把 id 打乱成同位宽的不可预测码——仅作示意。真实生产里这个置换必须是密码学强的(FPE 格式保留加密 / 一个小 Feistel 网络),否则像这里的线性置换,被采几个样本就能反推出规律。
3 · 补救 · 让短码既不撞、又不可枚举
三种主流方案,都在保住「短」的前提下消除「可猜」:
| 方案 | 怎么做 | 权衡 |
|---|---|---|
| 随机短码 | 不用 id,直接随机生成 6–8 位 base62(如 aB3xK)。 |
最简单;但要查重防撞(生成后查库,撞了重生成),或靠空间够大让撞概率可忽略。 |
| 加密自增 id | 内部仍自增(不撞 / 好分库),对外只暴露 encrypt(id) 的结果。 |
兼顾两者。Sqids / Hashids 只能阻止随手枚举,不是加密(顺序可被还原);需要强保证时用 FPE / Feistel。 |
| 自增 + 随机校验位 | 短码 = base62(id) 再拼几位随机后缀。 |
改动最小;但 id 段仍单调,规模 / 增速照样泄露,只挡住了「±1 猜邻居」。 |
能接受查库防撞就选随机方案,最简单;想保留自增的内部好处就用加密,需要强保证时用 FPE / Feistel,不要只依赖 Hashids。
最后一道底线:凡是真正私密的目标(私人相册、未发布草稿、内部文档),不要拿短码的「难猜」当门禁。短码再随机也只是 security through obscurity——链接一旦泄露就全开。访问控制(登录 / 权限 / 过期 / 一次性 token)必须另做一层,短码只管寻址。这正是分享入口页 open redirect 同一条原则的另一面:跳转这件事本身不可信任输入。
4 · 长 URL vs 短链 · 差在哪
| 维度 | 直接发原始长 URL | 发短链 |
|---|---|---|
| 字数受限渠道(短信 / 微博) | 占满甚至超限,拆多条 / 吃光字数 ✗ | 十几字符,正文随便写 ✓ |
| 二维码 | version 高、码点密,印小后难以识别 ✗ | version 低、稀疏,易扫描 ✓ |
| 口播 / 印刷 | 念不出来、折行、字号极小 ✗ | 一行大字、能逐字念 ✓ |
| 发出后改目标 | 已印 / 已发的改不了 ✗ | 改服务端映射表,全部跟随 ✓ |
| 来源统计 | utm 自带但落地页各算各的 ✗ | 跳转那一下统一记录 ✓ |
| 代价 | 无(但前几行的债都留着) | 多一跳 + 一张表 + 依赖短链服务存活 |
前三行(长度 / 可用性)是短链独有的存在理由;后两行(改址 / 统计)是它从 indirection 附带获得的好处,详见「为什么要有路由这一层」与「分享地址统一入口」。
短链的代价不容忽略:link rot。真实地址被藏在某一家短链服务背后——这家一旦关停(Google 的 goo.gl 已下线、不少免费服务转付费墙后清库),所有印在海报 / 名片 / 书里的短链随即全部变成死链,且无法挽救(你手里只有短码,原始 URL
在别人库里)。重要长期链接要么自建短链域名、要么直接用原始 URL。另外短链看不出真实目标,也常被钓鱼利用——这正呼应分享入口页的 open redirect:跳转目标必须可控、过 allowlist。
5 · 相关页
- 本系列 · 为什么要有路由这一层——短链是 indirection 的一个实例:这页讲清「稳定入口 ↔ 可变目标」解耦换来的改址 / 统计 / 灰度。
- 本系列 · HTTP 重定向——访问短码后「跳过去」那一下用的就是 3xx:短链多用
301(永久、可缓存)或302(临时)。 - 本系列 · 分享地址统一入口——把短码表做成一个真实的
/s/:code服务:命中 → 统计 → 查表 → 跳目标,含 open redirect 安全。 - Wikipedia · URL shortening · wikipedia.org——短链的来历、Twitter 140 字催生 t.co、以及 link rot 风险的综述。