本文摘要:yt-dlp 配了 Cookie 仍报 403,常因请求头未补齐。用 curl 逐头减法可区分 403 在会话层、头层还是鉴权层。站点接口变动后需重新验证头组合,URL 签名型接口补头无效。
一、问题与结论
批量抓取视频素材时最常见的挫败场景:从浏览器导出完整的 cookies.txt,yt-dlp 加上 --cookies 后运行,终端反复输出 HTTP Error 403: Forbidden。
核心结论 :Netscape 格式的 Cookie 文件只记录 name、value、domain、path、expiry 等字段,不包含 User-Agent、Referer、Origin。这三个头在 yt-dlp 中是三个互相独立的选项,需要手动指定--------cookies 解决登录状态,不解决客户端指纹;服务端校验的是完整请求上下文,缺任一关键头都可能被拒。
二、排查与选择依据
403 可能出现在三个层级,先定位层级再改配置:
| 层级 | 特征 | 修复方向 |
|---|---|---|
| 会话层 | 删掉 Cookie 后状态码变化 | 重新导出或刷新 Cookie |
| 请求头层 | 删掉 UA、Referer 或 Origin 后掉到 403 | 回填对应头到 yt-dlp 选项 |
| 服务端鉴权层 | 头和 Cookie 全对仍 403 | 查 IP/地区限制、URL 签名、提取器参数 |
定位方法是用 curl 做"逐头做减法":从浏览器 DevTools 的目标请求 Copy as cURL,逐个删除一个头,观察状态码在哪一步从 200 变 403。相比"逐个加头试",减法能直接告诉你缺了哪个。
替代方案与取舍
| 方案 | 选择条件 | 代价 | 边界 |
|---|---|---|---|
| curl 逐头减法定位 | 首次遇到 403,不确定失效层级 | 需 DevTools 抓包 | 只适用于请求头层和会话层;服务端签名鉴权需另行排查 |
--cookies-from-browser 直读 profile |
本地有可访问的浏览器 profile | 依赖平台解密组件,环境升级后可能失效 | 仍不携带 UA/Referer/Origin,不是万能方案 |
--proxy 对齐出口 IP |
curl 显示头全对仍 403 | 引入代理成本与稳定性问题 | 仅在判定为 IP/地区层时才值得 |
--extractor-args 调整提取行为 |
403 出现在提取器内部请求 | 站点级参数随版本变动,需与版本一起锁定 | 不解决会话层与头层问题 |
不建议使用该方案的场景:站点已启用 URL 请求签名(如带时效 token 的 CDN 接口),此时头不匹配只是表象,真正的鉴权在 URL 参数里,补头无效。
三、关键原理
Cookie 与请求头是两条正交的轴。Cookie 侧有 --cookies FILE 与 --cookies-from-browser BROWSER[+KEYRING][:PROFILE][::CONTAINER] 两个入口,两者搬运的都只有 cookie 记录本身;请求头侧是三个独立入口:

| yt-dlp 选项 | 覆盖的请求头 | 备注 |
|---|---|---|
--user-agent UA |
User-Agent |
指定自定义 UA |
--referer URL |
Referer |
README 写的是 "in some request"------作用范围是部分请求,不是全量 |
--add-header Origin:VALUE |
Origin 及任意其他头 |
格式 FIELD:VALUE,可多次使用;无 --origin 专用选项 |
两个容易忽略的细节:
-
Cookie 按域名作用域下发。从页面域导出的 Cookie 不会随请求发到分片所在的第三方域。如果 403 出现在下载阶段而非解析阶段,加头可能完全无效------你改的那三行头根本没出现在 403 的那条请求上。
-
Origin严格比对 scheme + host 。[https://www.example.com](https://www.example.com)与[https://example.com](https://example.com)是两个不同 Origin,http与https也不等价;--add-header冒号后的前导空格是否被 trim,以当前版本实测为准。
四、可运行示例
环境 :Windows / Linux 均适用(以下以 bash 为例);yt-dlp(版本以 yt-dlp --version 为准);ffmpeg(仅在合并音视频流时涉及)。
步骤 1:前置自检
bash
yt-dlp --version
yt-dlp --help | grep -E -- '--cookies|--cookies-from-browser|--user-agent|--referer|--add-header'
预期输出:应能 grep 到上述选项。若任何一项缺失,说明当前版本选项不同,先核对版本再排查 403。
步骤 2:复现 403(只加 Cookie)
bash
yt-dlp -v --cookies ./cookies.txt \
--format "bestaudio+bestvideo" \
-o '%(title)s.%(ext)s' \
"TARGET_URL" 2> step_A.log || true
grep -i '403\|forbidden' step_A.log
预期输出 :日志中出现 HTTP Error 403: Forbidden(错误前缀随版本可能不同,以实际输出为准)。
步骤 3:补齐三个头
bash
yt-dlp -v --cookies ./cookies.txt \
--user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
--referer "https://example.com/video-page" \
--add-header "Origin:https://example.com" \
--format "bestaudio+bestvideo" \
-o '%(title)s.%(ext)s' \
"TARGET_URL" 2> step_B.log || true
实际输出 :若 403 属于请求头层,step_B.log 中 403 消失、下载进度走完;若仍 403,进入步骤 4 定层。UA 的完整值请从 DevTools → Network → Request Headers 复制。
步骤 4:curl 逐头做减法定位
bash
#!/usr/bin/env bash
URL='PASTE_REAL_REQUEST_URL'
UA='PASTE_REAL_USER_AGENT'
REF='PASTE_REAL_REFERER'
ORG='PASTE_REAL_ORIGIN'
CK='PASTE_REAL_COOKIE_HEADER'
code() { curl -o /dev/null -s -w '%{http_code}' "$@" "$URL"; echo; }
CKH=(-H "Cookie: $CK")
echo "全头基准: $(code -H "User-Agent: $UA" -H "Referer: $REF" -H "Origin: $ORG" "${CKH[@]}")"
echo "删UA: $(code -H "Referer: $REF" -H "Origin: $ORG" "${CKH[@]}")"
echo "删Referer: $(code -H "User-Agent: $UA" -H "Origin: $ORG" "${CKH[@]}")"
echo "删Origin: $(code -H "User-Agent: $UA" -H "Referer: $REF" "${CKH[@]}")"
echo "删Cookie: $(code -H "User-Agent: $UA" -H "Referer: $REF" -H "Origin: $ORG")"
判读:
- 只有"删 Cookie"掉到 403 → 会话层问题,cookie 无效、过期或域不对,补头无效。
- 删 UA、Referer 或 Origin 掉到 403 → 对应头回填到
--user-agent/--referer/--add-header。 - 全对仍 403 → 不在请求头层,转查 IP/地区(
--proxy)或提取器参数(--extractor-args),不要再改头。
常见失败:所有结果均为 403 → 请求 URL 已过期(CDN 签名 URL 有时效),重新从 DevTools 复制最新 URL 再试。
关联场景:只有声音、播放列表重复下载
- 只有声音没有画面 :
--format选了bestaudio,或站点只提供单流格式。用-F列出可用格式后选择含视频的组合,或加--merge-output-format mp4配合 ffmpeg 合并。 - 播放列表重复下载 :加
--download-archive archive.txt记录已下载条目的内部 ID,重复运行时自动跳过;手动删除文件后仍会被跳过,需同步清理 archive 条目。
五、验证结果与边界
依据 yt-dlp README 与 yt-dlp --help 核对(发布前以当前版本复查):
--cookies与三个请求头选项为独立参数,不存在"一键复刻浏览器"的开关。- README 中
--referer写明只作用于 "some request",具体哪些请求携带 Referer 需在-v日志或抓包中确认。
待验证 :--add-header 对值中前导空格是否 trim;-v 打印的请求头粒度;不同站点对 Origin 的校验严格程度差异。
失效边界:
- 站点提取器变动后,请求头组合可能需重新调整;升级 yt-dlp 后应做同命令 A/B 对照。
--cookies-from-browser的后缀语法(如:PROFILE、::CONTAINER)在旧版本中不存在,误用表现为参数识别错误而非 403,两类问题不要混在一起诊断。- Cookie 过期后补再多头也无效------先确认 Cookie 是否仍然有效。
思考
- 在 CI 环境中
--cookies-from-browser不可用时,如何稳定维护 Cookie 与请求头的同步? - 站点启用 URL 签名后,请求头排查是否还有意义,还是应直接转向提取器参数层面?