订阅元数据传输机制:HTTP 响应头 Subscription-Userinfo 标准规范
直接答案:订阅流量并非写入在 YAML 配置文件正文内部,而是通过 HTTP 协议层的标准响应头
Subscription-Userinfo进行带外传输,实现了配置内容与动态计费元数据的解耦。
很多初学者打开下载到的 config.yaml 配置文件,反复搜索却找不到自己的剩余流量数字。这是因为代理生态遵循了优雅的协议分层设计原则:
[客户端发起请求] ---> GET /api/v1/client/subscribe?token=xxx HTTP/1.1
|
[服务端返回响应] <--- HTTP/1.1 200 OK
Subscription-Userinfo: upload=1073741824; download=10737418240; total=107374182400; expire=1781234567
Content-Type: text/yaml; charset=utf-8
[HTTP Body 消息体: 纯粹的 Clash YAML 节点与分流规则]
这种设计具备三大工程优势:
- 配置文件静态缓存友好:YAML 配置文件内容只在节点发生增减或规则调整时才会变动,客户端可以放心地将其写入本地磁盘;
- 毫秒级计费数据更新:用户的流量每时每刻都在发生变化,服务端每次下发响应时只需在 HTTP Header 中动态拼接最新的实时计费数字,避免反复重新序列化庞大的 YAML 树状文档;
- 跨客户端标准兼容:不仅是 Clash,包括 Surge、Shadowrocket、Quantumult X 以及 Sing-box 等各大客户端,均遵循同一套
Subscription-Userinfo解析规范,实现了全平台通用的无缝数据可视化展示。
核心四大字段原理解析:upload、download、total、expire 的字节换算与时间戳
直接答案:
Subscription-Userinfo采用分号分隔的键值对结构,所有流量数据统一以字节(Byte)为基准整数,时间戳采用自 1970 年以来的标准 Unix 秒数。
一个典型的生产级响应头示例如下:
Subscription-Userinfo: upload=2147483648; download=32212254720; total=107374182400; expire=1781222400
1. 字段定义与数据单位对照表:
| 标头参数名 | 数据物理含义 | 原始数据单位 | 换算为人类可读数值 | 换算公式与算法 |
|---|---|---|---|---|
| upload | 本计费周期内累计已上传数据量 | 字节 (Bytes) | 2.00 GB | $Upload_{GB} = upload div 1024^3$ |
| download | 本计费周期内累计已下载数据量 | 字节 (Bytes) | 30.00 GB | $Download_{GB} = download div 1024^3$ |
| total | 当前套餐分配的物理总流量上限 | 字节 (Bytes) | 100.00 GB | $Total_{GB} = total div 1024^3$ |
| expire | 本次订阅周期的绝对截止时刻 | Unix 秒级时间戳 | 2026-06-15 00:00:00 | 通过标准 UTC+8 时间转换 |
2. 剩余流量计算公式:
客户端在展示进度条时,采用以下数学公式: $$Traffic_{used} = upload + download$$ $$Traffic_{remaining} = total - Traffic_{used}$$ $$Percentage_{used} = \frac{Traffic_{used}}{total} \times 100%$$
若用户购买的是不限时按量计费套餐,服务端通常会将 expire 字段置空,或者设置为一个极远未来的时间戳(例如 2099 年),客户端检测到此特征后会自动在界面上显示为「永久有效」或「无到期日」。
主流客户端(Clash Verge Rev / Clash for Windows)的流量头解析与展示逻辑
直接答案:现代客户端在拉取配置成功后,会自动持久化存储响应头并在 UI 卡片上绘制实时进度条;若显示为「未知」或「NaN」,通常表示该标头缺失或格式解析异常。
不同客户端对流量信息头的呈现位置与交互逻辑存在细微差异:
[Clash Verge Rev Profiles 界面]
+-----------------------------------------------------------+
| 🟢 光速云 IEPL 专线主订阅 🔄 |
| 📊 已用: 32.00 GB / 100.00 GB (32%) [======------] |
| ⏳ 到期时间: 2026-11-15 18:30:00 |
+-----------------------------------------------------------+
1. Clash Verge Rev / Mihomo Party:
在左侧「订阅(Profiles)」列表中,每一个从远程 URL 成功拉取的订阅卡片底部,均清晰渲染出两行元数据:第一行展示环形或条形用量统计(已用流量 / 总配额),第二行显示精确到秒的到期时间。鼠标悬停在卡片上,还可弹出详细的上传与下载独立分项数据。
2. Clash for Windows(历史版本):
在 Profiles 面板中,CFW 会在卡片右侧以小字展示剩余流量折算值。若未显示,可右键卡片点击「Settings」,核对是否误勾选了「Hide User Info」。
为什么有些订阅无法显示剩余流量?服务端未注入响应头的原因剖析
直接答案:客户端不显示流量并非本地软件故障,90% 属于机场边缘 Nginx 反向代理未配置标头透传,或用户使用了第三方开源转换器截断了原始 HTTP Header。
当遇到导入成功却看不到任何流量信息时,可对照以下四大技术根因逐项排查:
- 反向代理 Nginx 未透传标头(Header Stripping):
机场运维在配置入口 CDN(如 Cloudflare)或前端反向代理 Nginx 时,若未显式添加
proxy_pass_header Subscription-Userinfo;,或者使用了过于激进的缓存策略,边缘节点会将非标准自定义 HTTP 响应头无情过滤剥离,导致客户端接收到的响应头中空无一物; - 使用了第三方订阅转换网站(Subconverter):
许多公共订阅转换器在向源站拉取数据并转译后,仅将 YAML 文本正文返回给客户端,而未将源站返回的
Subscription-Userinfo复制回写到客户端的响应中; - 本地脱机导入离线文件:
若通过浏览器手动下载
config.yaml后以本地文件(Local File)方式导入,由于没有经历 HTTP 请求交互过程,客户端无法捕获任何网络响应头,自然只能显示为离线纯配置; - 套餐状态异常或免费体验包限制: 部分小机场针对免费试用节点未开通自动计费接口,因此在响应中故意省略了该字段。
基于 cURL 与 Python 脚本远程排查流量头数据的实操演练
直接答案:无需打开客户端,在终端中直接运行携带自定义 User-Agent 的 cURL 命令,即可直接查看服务端原始下发的
Subscription-Userinfo响应头。
作为一名专业的网络维护者,当遇到流量显示疑难问题时,最纯粹的检测手段是在命令行中直接发起 HEAD / GET 探测:
1. 使用 cURL 命令快速审查响应头:
# 模拟 ClashMeta 客户端发起仅获取响应头的请求 (-I 表示 HEAD 请求)
curl -I -A "ClashMeta" "https://sub.airport.com/api/v1/client/subscribe?token=your_token_here"
若服务端配置合规,终端中会直接高亮输出包含计费信息的标头:
HTTP/2 200
server: nginx
date: Mon, 12 Oct 2026 06:15:00 GMT
content-type: text/yaml; charset=utf-8
subscription-userinfo: upload=104857600; download=5368709120; total=74088579072; expire=1781222400
2. Python 自动化解析与格式化脚本:
若想编写自动化监控脚本在流量不足 10% 时向企业微信或 Telegram 告警,可参考以下 Python 核心逻辑:
import requests
import datetime
sub_url = "https://sub.airport.com/api/v1/client/subscribe?token=your_token"
headers = {"User-Agent": "Clash-Verge/v1.7.0"}
res = requests.get(sub_url, headers=headers, stream=True)
user_info = res.headers.get("subscription-userinfo")
if user_info:
data = dict(item.split("=") for item in user_info.split("; "))
upload = int(data.get("upload", 0)) / (1024**3)
download = int(data.get("download", 0)) / (1024**3)
total = int(data.get("total", 0)) / (1024**3)
expire_time = datetime.datetime.fromtimestamp(int(data.get("expire", 0)))
print(f"✅ 上传: {upload:.2f} GB | 下载: {download:.2f} GB")
print(f"📊 总限额: {total:.2f} GB | 剩余: {total - upload - download:.2f} GB")
print(f"⏳ 到期时间: {expire_time.strftime('%Y-%m-%d %H:%M:%S')}")
else:
print("❌ 服务端未返回 subscription-userinfo 标头!")
具备实时透明流量反馈的标杆专线推荐
直接答案:透明的流量计量依赖于服务端严格透传标准的 Subscription-Userinfo 响应头,杜绝任何隐性扣费与虚标陷阱。
为了获得最透明、最清晰的用量把控体验,建议选用具备标准化响应头支持、绝无隐性扣费的标杆专线品牌:
光速云
★ 综合首选主推 ✓ 支持 Clash 订阅- 唯一同时标注 IEPL + 企业级内网专线(双线路架构)
- 唯一提供从约 8 元/月 (99 元/年) 到 680 元/月的 4 个不同价位套餐
- 节点覆盖 11 个核心地区,品牌资料中最全面
- 线路架构:IEPL 专线 + 企业级内网专线
AMM
流量预警与用量监控的自动化工作流搭建(Webhook 与通知)
直接答案:通过编写轻量级 Python 监控脚本定时抓取 Subscription-Userinfo,可实现当剩余流量低于 15% 或距离到期不足 3 天时自动向企业微信、钉钉或 Telegram 触发推送预警。
对于团队或重度生产力用户,突发断网可能带来不可估量的损失。借助现代无服务器函数(Serverless)或本地群晖 NAS,可以搭建优雅的无人值守流量监控机器人:
# 生产级流量余量 Webhook 告警示例逻辑
import requests
def check_traffic_and_alert():
sub_url = "https://sub.gsy.network/api/v1/client/subscribe?token=xxx"
r = requests.head(sub_url, headers={"User-Agent": "ClashMeta"})
info_header = r.headers.get("subscription-userinfo", "")
data = dict(x.split("=") for x in info_header.split("; ") if "=" in x)
upload = int(data.get("upload", 0))
download = int(data.get("download", 0))
total = int(data.get("total", 1))
remaining_ratio = 1 - (upload + download) / total
if remaining_ratio < 0.15:
# 触发企业微信机器人 Webhook
webhook_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_key"
msg = {
"msgtype": "text",
"text": {"content": f"⚠️ 警告:主力专线剩余流量已不足 {remaining_ratio*100:.1f}%,请及时续费!"}
}
requests.post(webhook_url, json=msg)
通过将该脚本挂载在 crontab 定时任务中(每天早上 09:00 执行一次),即可彻底摆脱每次都要打开客户端肉眼核对剩余流量的繁琐,实现真正从容的用网体验。
常见问题解答 (FAQ)
total=0 会误判为已欠费并锁死节点,因此服务端通过下发一个接近天文数字的字节限额来告知客户端无需限制用户的使用。&expand=false 以及确保使用的转换服务版本高于 0.7.2。现代版本的 Subconverter 在转发请求时会自动将上游返回的 Subscription-Userinfo 原样复制到对外的 HTTP 响应头中。Profile-Update-Interval 是另一个扩展的标准响应头,用以告知客户端建议的自动更新频率(单位为小时)。例如服务端下发 profile-update-interval: 24,客户端读取后会自动将定时静默刷新周期配置为 24 小时,避免用户客户端对服务端 API 造成过于密集的并发请求冲击。