Skip to content
Charles Shao
Go back

Header Bidding 深挖:Prebid.js(客户端)vs Prebid Server(服务端)架构

Updated:
views

程序化广告生态全景里,文中把客户端 SDK 称作”整条链路的扳机”,Header Bidding(头部竞价) 是它带来的最大变革。这篇文章把它讲透:它替代的旧逻辑是什么、一次客户端竞价(Prebid.js)的完整链路怎么跑、为什么又要把竞价搬到服务端(Prebid Server),以及 C2S vs S2S 这两种架构在 cookie 匹配率、延迟、成本、控制权上各让了什么步。

最后落到工程:Prebid Server 本质是一个高并发、低延迟的”扇出(fan-out)“服务——这也是为什么不少 Golang / 高并发岗位的 JD 会点名它。

TL;DR

Table of contents

Open Table of contents

1. 它要替代的旧逻辑:瀑布(waterfall)

Header Bidding 之前,媒体 Ad Server(典型是 Google Ad Manager / 早期 DFP)用瀑布流卖剩余库存——按事先排好的优先级,一个一个顺序询问需求源:

问广告网络 A(历史预估 eCPM $5)→ 要不要?不要 →
问广告网络 B(历史预估 eCPM $3)→ 要不要?不要 →
问广告网络 C(历史预估 eCPM $2)→ ……直到有人接单或兜底

三个致命问题:

一句话:瀑布的病根是”按历史顺位、串行询价”。Header Bidding 的全部价值,就是把它改成”按真实出价、并行竞价”。

2. 核心思路:提前 + 并行 + 公平

Header Bidding 把”卖剩余库存”这件事,从 Ad Server 内部抢到浏览器前端、并改成并行

在页面加载时、调用 Ad Server 之前,用一段 JS(典型是 Prebid.js同时向多个 SSP / 交易所发出竞价请求,在超时内收齐所有真实出价,取最高的那个,再作为”门槛价”交给 Ad Server 做最终决策。

三个关键词:

算笔账:同一次曝光,瀑布 vs Header Bidding

设三个需求源 A / B / C,按历史 eCPM 排序是 A($5)> B($3)> C($2);但这一次它们的真实出价是 A $2、B $4、C $8:

机制发生了什么媒体到手
瀑布按历史顺位先问 A → A 这次出 $2、有填充 → 命中即停$2
Header BiddingA / B / C 并行报真实价 $2 / $4 / $8 → C 价高者得$8

差距 +300%:C 按历史 eCPM 排在最末、瀑布根本问不到它,可它这次恰恰出价最高。Header Bidding 让真实价同台,才把这笔钱捞回来——这就是”提前 + 并行 + 公平”省下的真金白银。

3. 客户端 Header Bidding:Prebid.js 的完整链路

Prebid.js 是开源的客户端 Header Bidding 库,事实上的行业标准。一次客户端竞价端到端是这样:

客户端 Header Bidding 时序图。浏览器加载页面,Prebid.js 启动并并行向 SSP-A / SSP-B / SSP-C 同时发出 bid request;各 SSP 在超时(如 1000ms)内返回各自出价;Prebid.js 收齐出价、选出最高价,写成 hb_pb 等 key-value;浏览器的 GPT(googletag)携带这些 key-value 调用媒体 Ad Server(GAM);Ad Server 用 hb_pb 匹配 line item,与直采订单、AdX 一起做最终决策,返回胜者;Prebid 渲染胜出的 creative。若超时则按 failsafe 直接走 Ad Server 注意第 4、5 步:Prebid 自己不投广告,而是把竞价结果翻译成 Ad Server 能识别的 key-value,借 Ad Server 现有的决策机制落地。

文字版分解:

  1. 页面加载,Prebid.js 启动,读取广告位(ad unit)和接好的 bidder adapter 配置。
  2. 并行询价pbjs.requestBids() 同时向所有配置的 SSP 发出 bid request,各 SSP 内部跑自己的竞价。
  3. 收口 + 超时:在 PREBID_TIMEOUT(通常 1000–1500ms)内收齐能回的出价;超时未回的直接丢弃——超时是性能与收益最直接的旋钮
  4. 选最高价 + 写 key-value:Prebid 选出最高价,用 pbjs.setTargeting 把它编码成一组 targeting key 挂到广告请求上:
hb_pb     = 6.20         # 价格分桶(price bucket)
hb_bidder = sspB         # 中标 bidder
hb_adid   = a1b2c3       # 对应 creative 的 id
hb_size   = 300x250
  1. 调用 Ad Server:浏览器的 GPT(Google Publisher Tag) 携带这些 key-value 调用 GAM。
  2. 最终决策:Ad Server 用 hb_pb 去匹配预先建好的 line item(按价格分桶建的订单),让 Header Bidding 的价和直采订单、自家 AdX 一起做 decisioning;谁的 eCPM 高谁赢。
  3. 渲染:若 Header Bidding 的某个 bidder 胜出,Ad Server 返回的 creative 会触发 Prebid Universal Creative 去把缓存的中标素材渲染到页面。

工程精髓在第 4–6 步:Header Bidding 不绕开 Ad Server,而是把自己”翻译”成 Ad Server 听得懂的 key-value,复用它的 line item 与 decisioning。这也意味着——媒体要在 GAM 里预先建一大批按价格分桶的 line item(比如每 $0.10 一档),hb_pb 的分桶粒度必须和 line item 对齐,否则价格匹配会错位。

4. 服务端 Header Bidding:Prebid Server(S2S)

客户端方案有个硬伤:每接一个 bidder,浏览器就多一份 adapter JS、多一次出站请求。接 5 个 SSP 还行,接 15 个,页面在加载关键路径上就被拖垮——延迟、流量、电量全是用户在买单。

Server-to-Server(S2S)Header Bidding 的解法是把”并行扇出询价”这件重活,从浏览器搬到一台服务器上——这台服务器的开源实现就是 Prebid Server(PBS)

服务端 Header Bidding 架构图。浏览器里的 Prebid.js 只向 Prebid Server 发出一次请求;Prebid Server 在服务端把请求扇出(fan-out)并行调用多个 bidder adapter(SSP-A / SSP-B / SSP-C ...),在统一的服务端超时预算内收齐出价;Prebid Server 做币种换算、底价、隐私合规(TCF/GDPR)处理后,把最高价聚合返回给浏览器;浏览器再把胜出价作为 key-value 交给媒体 Ad Server 做最终决策。旁注:服务器没有用户的买方 cookie,需要 cookie syncing 弥补匹配率 浏览器侧从”扇出 N 个请求”塌缩成”1 个请求”,扇出的活儿移到 Prebid Server 内部并行完成。代价是服务器不在用户浏览器里,拿不到各买方的 cookie。

流程的差别只在前半段:

  1. 浏览器里的 Prebid.js(或轻量 stub)只向 Prebid Server 发一次请求
  2. Prebid Server 在服务端把请求扇出,并行调用所有配置的 bidder adapter;
  3. 服务端统一超时内收齐、聚合,做币种换算 / 底价 / 隐私合规等处理;
  4. 把最高价(或一组候选)返回浏览器;
  5. 后半段和客户端一样:写 key-value → 调 Ad Server → 决策 → 渲染。

Prebid Server 有两套开源实现:PBS-Go 和 PBS-Java。 高流量媒体 / SSP 多选 Go 版——这也是”Golang + 高并发 AdTech”岗位常点名它的原因,第 6 节细讲。

5. C2S vs S2S:到底各让了什么步

两种架构不是谁取代谁,而是一组权衡。核心矛盾是一句话:把竞价放在浏览器(离用户近、有 cookie)还是放在服务器(快、可扩展、但没 cookie)。

维度客户端 Prebid.js(C2S)服务端 Prebid Server(S2S)
竞价在哪跑用户浏览器媒体 / SSP 的服务器
浏览器请求数每个 bidder 一次(N 次)仅 1 次(到 PBS)
页面延迟 / 性能差——adapter JS + N 次请求堆在关键路径好——扇出在服务端并行
cookie 匹配率——浏览器里有各买方 cookie——服务器没有,需 cookie syncing 弥补
可接 bidder 数量受性能限制,通常 ≤ 5–10可接很多,扇出在服务端
典型成交价(CPM)偏高(匹配率高)偏低(匹配率损耗),靠规模与速度补
控制 / 可调试前端可见、易调黑盒在服务端,需日志
维护成本页面里堆 adapter维护一套高并发服务

5.1 绕不开的痛点:cookie syncing

S2S 最大的损耗来自 cookie 匹配率。买方(DSP / SSP)靠自己域名下的 cookie 认出”这是谁”。客户端竞价发生在用户浏览器里,请求天然带着各买方的第三方 cookie,匹配率高;而 Prebid Server 跑在媒体的服务器域名下,它手里只有自己的 ID,认不出对方的用户

弥补办法是 cookie syncing(cookie 同步):Prebid Server 通过 /cookie_sync/setuid 端点,借用户浏览器发起一轮重定向 / 像素,把”PBS 的用户 ID ↔ 各 bidder 的用户 ID”在 PBS 的 uids cookie 里对应起来。下次 S2S 请求就能带上各 bidder 认得的 ID。

但 cookie syncing 正在结构性失效:Safari ITP、Firefox ETP 早已封杀第三方 cookie,Chrome 也在收缩。匹配率下降是 S2S(乃至整个第三方 cookie 体系)的长期趋势,这也把行业推向第一方数据与统一 ID(UID2 / ID5 等)作为替代——这部分留给身份专题展开。

5.2 现实:混合(hybrid)才是常态

实际生产里很少非此即彼。媒体通常两套并跑:把匹配率敏感、出价高的核心 bidder 留在客户端,把长尾的、性能敏感的 bidder 放到服务端——按”这个 bidder 在客户端还是服务端更划算”逐个分流。Prebid.js 原生支持把部分 bidder 标为 S2S,无缝混合。

6. 工程深挖:Prebid Server 是个什么系统

从工程视角,Prebid Server 是一个典型的”扇出 / 收口(scatter-gather)“高并发服务——它的难点不在业务逻辑,而在用一个固定的延迟预算,把一次请求同时打到几十个外部 bidder、再把能回的结果在预算内收齐。这也是它和”百万 QPS 广告服务”同属一类工程问题的原因。

Prebid Server 内部扇出/收口架构图。一次 auction 请求进入 Prebid Server,先经过解析与隐私合规(TCF/GDPR、US Privacy)处理;然后扇出(fan-out)阶段并发地把请求分发给多个 bidder adapter(每个 adapter 把统一的 OpenRTB 请求翻译成该 SSP 的私有格式、发起 HTTP 调用),每个 adapter 受一个 per-bidder timeout 约束;收口(gather)阶段在统一的 auction timeout 预算内收集已返回的 bid,超时未回的被丢弃;之后做币种换算、底价过滤、bid 校验,必要时把 VAST/创意写入 Prebid Cache;最后聚合排序,返回最高价。旁注:固定超时预算 + 优雅降级是核心——慢 bidder 不能拖垮整场 核心命题:在一个固定的超时预算里,向 N 个外部系统并发取数、能收多少是多少、且任何一个慢 bidder 都不能拖垮整场。这正是 Go 的 goroutine + context 超时模型擅长的场景。

几个值得讲的工程点:

一句话给工程师:Prebid Server 把”程序化竞价”抽象成了一个’带截止时间的并发聚合’问题。理解它,基本就理解了高并发 AdTech 后端的缩影——这也是为什么相关 JD 会拿它当试金石。

7. 竞价价落在哪:媒体 Ad Server 的决策

Header Bidding 跑完得到的胜出价,不直接投广告——它被翻译成一个 price priority(价格优先)line item,由 hb_pb 的金额带着,进入媒体 Ad Server(GAM)做最终决策。Ad Server 的活儿是 decisioning(分配):在直采保量单、各种程序化、Header Bidding、兜底广告之间,挑出”既满足合约、收益又最高”的那一个。

对 Header Bidding 来说,有两点必须知道:

媒体 Ad Server 本身是个值得单独展开的大角色——它的 line item 优先级体系、decisioning 流水线、dynamic allocation 与统一竞价、以及作为高 QPS 决策系统的工程内核,完整拆解见专文《媒体 Ad Server 深挖:GAM 的 decisioning、line item 与 dynamic allocation》。这里只需记住:Header Bidding 不绕开 Ad Server,而是把自己翻译成它听得懂的 line item,借它的决策落地。

8. 关键工程权衡

把散落各处的取舍集中一下:

8.1 一个真实的超时调优:别用一个全局 timeout 套所有 bidder

“timeout 设多少”是 Header Bidding 最被拍脑袋的旋钮。问题在于:各 bidder 的延迟分布天差地别,一个全局 timeout 对所有人是同一条死线,但对收益的影响却各不相同。 看一组有代表性的数(5 个 bidder,按真实时延分布画像):

bidderp50p95平均贡献 eCPM全局 800ms 下的命运
A120 ms280 ms$2.10稳进
B200 ms450 ms$3.40稳进
C350 ms1100 ms$6.80p95 超界 → 约一半请求被丢
D90 ms180 ms$1.20稳进
E600 ms1500 ms$0.90几乎全超时,却仍发了请求拖慢页面

一个全局 800ms 同时犯了两个方向的错:

正确做法是按每个 bidder 的 p95 分桶、分别给 per-bidder 预算,并把不达标的移到 S2S

快档(p95 < 300ms):A、D      → 客户端,per-bidder 300ms
中档(p95 < 500ms):B          → 客户端,per-bidder 500ms
慢但高价(C):                  → 移到 S2S,单独给 1200ms per-bidder 预算
                                   (S2S 的慢不占浏览器关键路径)
慢又低价(E):                  → 直接下掉,它的期望收益盖不住它的延迟成本

前后对比的逻辑很清楚:C 从”约一半丢弃”变成”几乎全收”,把它 $6.80 里被漏掉的那部分捞回来;E 被移除后,A/B/D 的填充率回升。净效果通常是整体 eCPM 上行、而页面 p75 加载时间反而下降——因为你拿掉了”既慢又不赚”的纯负担,又把”慢但值钱”的挪到了不挡路的服务端。

一句话原则:timeout 不是一个数字,而是一张按”延迟分布 × 期望收益”决策的表。 全局 timeout 是”一刀切”的偷懒;per-bidder 预算 + C2S/S2S 分流,才是把延迟预算花在刀刃上的姿势。这也正是 §6 里 Prebid Server 给每个 adapter 套独立 per-bidder timeout 的意义所在。

9. 工程实战:几个绕不开的坑

一个典型的收益事故:某媒体上线一个新 bidder 后整体收入不升反降。排查发现这个 bidder 的 p95 响应要 1200ms,而 auction timeout 设在 1000ms——它几乎每次都超时被丢,却又在客户端实打实发起了请求、拖慢了页面,导致别的本可成交的 bidder 也跟着掉填充。把它移到 S2S、并单独给更宽的 per-bidder 预算后,才转正。慢 bidder 不只是”自己不赚”,它会拖累整场。

其余高频坑:

10. 常见误解 ↔ 正解

这一节专治”听起来对、其实差一点”的高频误区——也是面试里最容易露馅的地方:

常见误解正解
Header Bidding 自己会投广告不投——它把胜价写成 hb_pb,借 Ad Server 的 line item 落地
Header Bidding 绕开了 Ad Server恰恰相反,它复用 Ad Server 的 decisioning,把自己翻译成 line item
HB 这次出价最高就一定拿到曝光还要在 Ad Server 里和保量直采比,保量优先级更高(见 §7)
S2S 一定比 C2S 好(更快就更赚)快,但 cookie 匹配率低、CPM 可能更低;常态是混合
接的 bidder 越多越赚客户端每多一个 adapter 拖页面;慢 bidder 还拖累整场(见 §9)
timeout 调大就多赚过大拖慢渲染、伤可见率;要按 p95/p99 分 bidder 调
Prebid 是二价拍卖现代 Prebid 默认一价
cookie syncing 能长期解决匹配率它在结构性失效(ITP/ETP),趋势转向第一方 / 统一 ID

11. 速查表

Header Bidding 的本质,是把卖方侧的竞价从一个黑箱顺位,改造成一场公开、并行、按真实价决胜的拍卖。Prebid.js 把它放进浏览器,Prebid Server 把它搬上服务器——理解这两者的取舍,基本就握住了现代卖方变现技术栈的主轴。


延伸阅读

按”全局 → 端上扳机 → 决策落地 → 透明度”的顺序串起来读最顺:

再是规范与一手资料:

附录:术语表


views
Share this post on:

Previous Post
拍卖机制与 Bid Shading:一次竞价的漏斗、底价、结算,与一价时代为什么要「主动少出一点」
Next Post
OpenRTB 协议精讲:bid request / response 对象模型、拍卖结算与 win / loss / billing notice