做推广、发内容、投广告的人迟早会碰到一件事:短链接不可控。
用的第三方短链服务,可能哪天开始给你插广告页、可能突然关停让你的链接全变死链、也可能把你的点击数据拿去卖。更麻烦的是,短链通常是要印在物料上、写进二维码里的,改不了。
自建短链服务解决的就是这个:链接归你,数据归你,域名也就是你自己的品牌域名。
这篇讲三个主流方案怎么选,以及 Shlink 的完整部署。
| Shlink | YOURLS | Kutt | |
|---|---|---|---|
| 技术栈 | PHP + 独立 Web 前端 | PHP + MySQL | Node.js |
| 首次发布 | 2019 | 2009 | 2020 |
| 界面 | 现代(React) | 偏老 | 现代、简洁 |
| 多域名 | ✅ 单实例支持 | 插件 | ❌ |
| 点击统计 | 完整(地理、设备、来源) | 基础,靠插件增强 | 基础 |
| 二维码 | ✅ 内置 | 插件 | ❌ |
| 地理定位 | ✅ GeoLite2 | 插件 | ❌ |
| 机器人识别 | ✅ | ❌ | ❌ |
| 链接过期/限次 | ✅ | 插件 | ✅ |
| 密码保护链接 | ✅ | 插件 | ✅ |
| 容器数量 | 3(API + 前端 + 库) | 2 | 2-3 |
| 资源占用 | 中 | 最低 | 低 |
怎么选:
- 要统计、要 API、要多域名 → Shlink。2026 年 1 月刚发了 5.0,还在活跃开发
- 只要最简单的短链,机器越省越好 → YOURLS。十几年的老项目,一个 PHP + MySQL 就跑起来,插件两百多个
- 自己用,界面好看就行,不需要复杂统计 → Kutt
下面重点讲 Shlink,另外两个的差别主要在功能多少,部署套路类似。
这一步比装软件重要得多,而且开弓没有回头箭 —— 短链一旦发出去,域名就换不了了。
越短越好,这是短链的全部意义。几个思路:
- 子域名:如果你已经有
example.com,用s.example.com或go.example.com。零成本,缺点是没比原域名短多少 - 短后缀域名:
.link、.cc、.gs、.me这类,能拼出四五字符的极短域名。一年十几到几十块 - 品牌相关:
品牌名.link这种,既短又能强化品牌
有几个坑要避开:
别用免费后缀。 .tk、.ml、.ga 这些免费域名在邮件系统和社交平台上信誉极差,你发出去的链接可能直接被拦。省下的钱远不够赔的。
注意域名历史和信誉。 买之前查一下这个域名有没有被用于垃圾邮件或者灰产 —— 二手域名特别容易踩这个雷。被拉黑的域名做短链,等于白做。
别用太偏门的后缀。 有些后缀部分浏览器和 App 不识别,用户点了打不开,或者被安全软件拦下来。
短链服务的资源消耗很低,1 核 1G 完全够用,甚至有点富余。
真正要关注的不是配置,是这两点:
网络延迟。 短链的本质是"用户点一下,服务器跳一下",这一跳的延迟直接算在用户体验里。服务器离用户越近越好,国内用户为主就选国内或亚洲节点。
带宽和流量。 每次点击都是一次请求,如果链接投放在大流量渠道(信息流广告、大号推广),QPS 可能不低。但这属于"真的火了才需要担心"的问题,一般推广量 1Mbps 带宽绰绰有余。
还有个隐性要求:域名要能正常访问。 有些域名在国内被墙过,或者被微信/QQ 拦截。发短链之前先在各平台点一遍试试。
Shlink 是三个容器的组合:API 服务、Web 管理界面、数据库。
准备目录和密码:
mkdir -p ~/shlink/db && cd ~/shlink
docker-compose.yml:
services:
shlink:
image: shlinkio/shlink:stable
container_name: shlink
restart: unless-stopped
environment:
DEFAULT_DOMAIN: s.example.com
IS_HTTPS_ENABLED: "true"
GEOLITE_LICENSE_KEY: 你的MaxMind密钥
DB_DRIVER: maria
DB_HOST: shlink-db
DB_PORT: 3306
DB_NAME: shlink
DB_USER: shlink
DB_PASSWORD: 换成一个强密码
INITIAL_API_KEY: 换成一串长随机字符
TZ: Asia/Shanghai
ports:
- "127.0.0.1:8080:8080"
depends_on:
- shlink-db
shlink-db:
image: mariadb:11
container_name: shlink-db
restart: unless-stopped
environment:
MARIADB_DATABASE: shlink
MARIADB_USER: shlink
MARIADB_PASSWORD: 换成一个强密码
MARIADB_ROOT_PASSWORD: 换成一个不一样的强密码
volumes:
- ./db:/var/lib/mysql
shlink-web:
image: shlinkio/shlink-web-client
container_name: shlink-web
restart: unless-stopped
environment:
SHLINK_SERVER_URL: https://s.example.com
SHLINK_SERVER_API_KEY: 跟上面的 INITIAL_API_KEY 保持一致
SHLINK_SERVER_NAME: 我的短链
ports:
- "127.0.0.1:8081:8080"
几个关键点:
DEFAULT_DOMAIN 不要带协议和结尾斜杠。 写 s.example.com,不是 https://s.example.com/。写错了会出现"创建成功但访问 404"的情况。
INITIAL_API_KEY 只在容器首次启动、且数据库里还没有任何 API key 时才生效。 它创建的是一个管理员密钥。如果你后来改了这行再重启,旧的 key 不会被删除,新的也不会创建 —— 这时候要进容器手动管理:
docker exec -it shlink shlink api-key:list
docker exec -it shlink shlink api-key:generate
docker exec -it shlink shlink api-key:disable 旧key
SHLINK_SERVER_URL 必须是浏览器能访问到的公网地址。 Web 客户端是在你自己的浏览器里运行的,它不走 Docker 内网。填内部主机名会连不上。
GEOLITE_LICENSE_KEY 是可选的。 想要访客地理统计就去 MaxMind 免费注册一个 key。注意 Shlink 启动时会自动下载 GeoLite2 数据库,MaxMind 限制大约每天只能下一次,所以容器频繁重启可能下载失败 —— 不影响短链功能,只是地理数据暂时缺失。
启动:
docker compose up -d
docker compose logs -f shlink
看到服务正常启动就成了。
短链必须走 HTTPS,没有商量余地 —— 用户看到 http 开头的链接会觉得可疑,很多平台也会拦截。
Caddy 配置:
s.example.com {
encode gzip
reverse_proxy 127.0.0.1:8080
}
admin.example.com {
basicauth {
admin $2a$14$你的密码哈希
}
reverse_proxy 127.0.0.1:8081
}
管理界面(8081)一定要加认证。 它是你所有短链的控制台,暴露出去等于把数据送人。用 Caddy 的 basicauth 挡一层,或者用 Cloudflare Access。
跳转服务(8080)不能加认证,否则用户点链接会被拦下来 —— 它必须对所有人开放。
Caddy 的完整写法看这篇 Caddy 反向代理完全指南,用 Nginx Proxy Manager 的话参考这篇。
Shlink 有三个入口,日常用哪个看场景。
网页界面是最直观的。打开管理界面,粘贴长链接,可以:
- 自定义短码(
/launch2026比/aB3xK9好记得多,推广物料强烈建议自定义) - 设置有效期(到期自动失效)
- 设置点击上限(比如优惠券链接限领 1000 次)
- 生成二维码,直接下载
命令行适合批量:
docker exec -it shlink shlink short-url:create 'https://example.com/very/long/path?utm_source=xxx' \
--custom-slug launch2026 \
--max-visits 1000 \
--valid-until 2026-12-31T23:59:59+08:00
REST API 适合接自动化 —— Shlink 是 API-first 设计的,所有功能都能通过接口调用。如果你要批量生成几百个带 UTM 的短链,写个脚本调 API 比手点快得多。
还有个实用的小东西:Shlink 有官方浏览器扩展,在浏览器里点一下就能把当前页面转成短链,写文章的时候很顺手。
这是自建短链相对第三方服务的核心价值 —— 数据完全在你自己手里。
Shlink 记录的信息包括:
- 点击时间分布:哪个时间段点击最多
- 来源分类:直接访问、社交平台、搜索引擎、二维码扫码
- 地理位置:国家/城市(需要 GeoLite2)
- 设备信息:桌面还是移动端,浏览器类型
- 独立访客与重复访问的区分
有一个设置必须提醒:不要改成 301 重定向。
Shlink 默认用 302(临时重定向)。302 的意思是"这个地址是临时的",浏览器每次访问都会回到你的服务器问一下,你才能统计到每一次点击。
如果你为了所谓"SEO 权重传递"改成 301,浏览器会缓存这个跳转,第二次之后的点击根本不会到达服务器,统计直接失真。短链的场景下,302 才是正确选择。
短链服务有个特殊风险:你的域名会替别人背锅。
如果有人拿你的短链去做钓鱼、诈骗、发垃圾广告,被举报封禁的是你的域名。域名一旦进了各种黑名单,你后面所有正常业务都会受影响,解封非常麻烦。
防护措施:
别开放公开注册。 Shlink 本身不提供公开注册功能,但如果你把 API key 泄露出去,别人就能用你的服务。API key 当密码一样对待。
加个前置层。 把域名接到 Cloudflare 上,开 WAF 和速率限制,能挡掉大部分自动化滥用。
定期看一眼数据。 如果发现你不认识的短链被创建了,或者某个链接的点击量异常暴涨,赶紧查。
给 API 加限额。 如果确实需要让别人用,用 Shlink 的 API key 权限机制限制范围,别给管理员 key。
要备份的就两样:数据库(所有短链记录和统计数据)和 compose 文件(配置)。
因为数据库挂在 ./db 目录下,直接打包就行:
cd ~/shlink
docker compose stop shlink-db
tar czf ~/shlink-backup-$(date +%F).tar.gz db docker-compose.yml
docker compose start
数据库不大的话,更推荐用 mysqldump 做逻辑备份,不用停服务:
docker exec shlink-db mariadb-dump -u shlink -p shlink > ~/shlink-$(date +%F).sql
短链数据的备份比大多数服务都重要。 别的服务挂了重新配一遍就行,短链记录丢了意味着你已经发出去的所有链接都成了死链 —— 印在包装上的、投在广告里的、写在名片上的,全废。
创建的短链访问 404:DEFAULT_DOMAIN 跟实际访问的域名对不上。注意它不能带协议和斜杠。
点击统计一直是 0:检查是不是把重定向改成了 301,或者前面套了一层 CDN 把跳转缓存了。
管理界面连不上服务器:SHLINK_SERVER_URL 填的是内部地址。它必须是浏览器能打开的公网 URL。
GeoLite 下载失败:MaxMind 限流,等一天再重启。或者设 SKIP_INITIAL_GEOLITE_DOWNLOAD=true 先跳过,不影响使用。
短链被微信/QQ 提示"已停止访问":域名被平台拦截了。换域名,或者申诉。这个和你的服务无关,是域名信誉问题。
想换域名怎么办:Shlink 支持多域名,可以新老并存。老的继续解析(保证已有链接不失效),新的用于后续创建。千万别直接换掉,已经发出去的链接会全部失效。
自建短链的价值不在省钱 —— 第三方免费短链多得是。价值在于控制权和数据:链接永远有效、跳转页永远是干净的、点击数据只有你自己知道。
技术门槛不高,三个容器的事。真正需要花心思的是域名选择和防滥用这两件非技术的事 —— 前者决定了短链好不好用,后者决定了你的域名会不会被搞臭。
延伸阅读:
