On 20 March 2025 the IAB Tech Lab — the body that writes the standards the entire digital ad industry runs on, from OpenRTB to ads.txt to VAST to the TCF consent framework — announced an open-source project called Trusted Server. Its pitch, in the CEO's words, was that publishers should "run their own advertising middle layer."
Seventeen months later that repository is roughly 137,000 lines of Rust and 15,000 lines of TypeScript, with pull-request numbers past #1,000 and new commits every few days. We read all of it.
Here is what the industry's standards body built, described in its own source files:
- It rewrites every third-party ad URL on the page so the request goes to a domain the browser treats as first-party.
- It proxies Google Publisher Tag's entire script cascade —
gpt.js, pubads_impl.js, the lazy sub-modules, the viewability pings.
- It installs a six-layer client-side shim that catches scripts as the page tries to insert them and rewrites their URLs before the browser ever sees the original.
- It issues server-set identity so bidders still recognise the visitor without third-party cookies, and carries that identity through redirect chains.
- It rewrites creative HTML and CSS —
img, srcset, iframe, video, object, url() — and re-signs every hop.
That is AdUnblock's architecture. Not "similar to." That is the list.
We have never spoken to anyone on that project. They have never seen our code. Two teams arrived at the same five things because those five things are the problem. This post is about what that convergence proves — and about the single place where the two designs part company, which happens to be the only place that decides whether recovery is still working in six months.
What Trusted Server actually is
Be clear about the framing, because it matters for everything that follows: Trusted Server is not primarily an ad-blocking product. It is an answer to signal loss.
The IAB Tech Lab's own page gives three motivations: browser telemetry "tending to zero," publishers lacking data governance over their own sites, and — third on the list — "rising ad-blocking rates." The press release phrases the relevant benefit as: "Server-Side Ad Requests: Eliminates client-side dependencies and mitigates ad-blocking threats."
Technically it is a Rust application compiled to WebAssembly and deployed to an edge runtime — with shipped adapters for Fastly Compute, Cloudflare Workers, Fermyon Spin, and a native dev server. The publisher runs it themselves, in front of their own site, under their own domain. It is Apache-2.0 licensed and free. Integrations already exist for GPT, Prebid, Amazon's APS, Permutive, Didomi, Osano, Sourcepoint, DataDome and Google Tag Manager.
It is also, in the project's own words, not finished. The version number is still 0.1.0. The current unreleased changelog carries six separate entries marked "Breaking." The original FAQ is blunter than most vendors would dare to be: "NO. It is NOT a version 1.0 and is NOT ready for use by a publisher in its current form."
None of that undercuts the point. The point is what a room full of the industry's engineers concluded the answer had to look like.
The five things both systems do
1. Every third-party URL gets rewritten to a proxy
The module documentation in creative.rs reads like a checklist we wrote from scratch three years ago:
<img src>, data-src, [srcset], [imagesrcset] · <script src> · <video src>, <audio src>, <source src> · <object data>, <embed src> · <input type="image" src> · SVG <image href|xlink:href>, <use href> · <iframe src> · <link rel~="stylesheet|preload|prefetch"> · inline [style] and <style> blocks: url(...) values are rewritten
Relative URLs are left alone. data:, javascript:, blob: are skipped. Every one of those decisions is a decision we also had to make, in the same order, for the same reasons.
2. Google's script cascade is proxied, not just the entry point
GPT is not one file. It is a bootstrap that loads a 640 KB implementation that lazily loads sub-modules that load viewability and error reporting. Miss any link in that chain and the slot goes dark. The GPT integration documents the cascade explicitly and proxies all of it.
3. A client-side shim lies to the page
This is the part that surprised us most, because it is the part everyone underestimates. Once you rewrite the ad stack's URLs, the ad stack keeps writing new ones at runtime — and you have to catch them all before the browser resolves them.
The comment block at the top of script_guard.ts enumerates six interception layers:
| Layer | What it catches |
|---|
document.write / writeln | GPT's primary sync loading path — a <script> injected into the parser stream |
HTMLScriptElement.prototype.src descriptor | script.src = url |
setAttribute patch | script.setAttribute('src', url) |
document.createElement patch | per-instance fallback when the prototype descriptor won't install |
| DOM insertion dispatcher | appendChild / insertBefore at insertion time |
MutationObserver | anything added via innerHTML, .append(), or attribute mutation |
We have the same six. We arrived at them the same way anyone does: one production bug at a time.
The file's own summary of why it exists is the sentence we would use: "this guard is the sole mechanism that routes GPT's cascaded script loads back through the first-party proxy." The architecture doesn't work without it.
4. Identity is re-scoped server-side
Header bidding without identity is worth a fraction of header bidding with it. Trusted Server issues an edge cookie (ts-ec), sets it server-side, and appends it to every proxied request — including on each hop of a redirect chain, up to four hops deep. Same problem, same shape of answer. We solve it with an opaque cookie-name codec instead of a query parameter; the goal is identical.
5. The unglamorous 80%
Redirect chains, srcset descriptors, CSS url() values, 1×1 pixel detection, byte-range video, content-type branching, an SSRF allowlist on redirect targets. None of it demos well. All of it is where fill rate actually comes from.
What the convergence proves
Publishers ask us a fair question, and they should: is this a trick that stops working?
The most credible answer we can give is not our own architecture diagram. It is that when the IAB Tech Lab convened a task force, took contributions from Fastly and Equativ and Prebid, and spent seventeen months and 137,000 lines of Rust designing how publishers should serve ads under browser lockdown — they built a proxy that rewrites ad URLs to a domain the blocker doesn't recognise, and a client-side shim that lies to the page about what those URLs are.
Ad recovery is not a loophole. It is the industry's own reference architecture. We just happen to have been running it in production, on live publisher inventory, since before there was a spec for it.
Where the two designs part company
One line. Trusted Server's proxy endpoint looks like this:
https://publisher.com/first-party/proxy
?tsurl=https%3A%2F%2Fsecurepubads.g.doubleclick.net%2Ftag%2Fjs%2Fgpt.js
&tstoken=<signature>
Three properties of that URL, all deliberate:
The domain is the publisher's own. That is the entire premise — "first-party context" is the product name, essentially. It is also genuinely the strongest thing about the design, and we'll come back to it.
The path is a fixed literal. /first-party/proxy. Also /first-party/click, /first-party/sign, /integrations/gpt/script, /integrations/gpt/pagead/*, and the client bundle at /static/tsjs=tsjs-unified.min.js. Every deployment on earth uses the same strings, and the strings are published in a public repository with a search box.
The destination is in cleartext. tsurl= carries the real target URL, percent-encoded. Percent-encoding escapes : and / — it does not escape dots or letters. So the substring securepubads.g.doubleclick.net appears verbatim in the query string of a request the browser is about to make.
The tstoken is not hiding anything, and was never meant to. Reading the signing code, its job is integrity: it stops a stranger from turning your ad proxy into an open relay. That is a real and necessary security control. It is not concealment, and the project has never claimed otherwise. It is called Trusted Server. Being legible is the point.
Ours goes the other way. Our proxy hosts are ours, not the publisher's; our script path rotates; our target parameter is an opaque token with no readable substring in it. Which is the right call depends entirely on what you think the adversary is — and that is the actual disagreement between these two designs.
What a filter list does with a fixed string
Here is where we can stop reasoning and show data, because we get blocked for a living.
Between 31 May and 15 August 2026 — 76 days — our automated mitigation pipeline recorded:
| Event | Count |
|---|
| Proxy hosts marked blocked by a new filter rule | 38 |
| Fresh domains assigned to replace them | 39 |
| Global script-path rotations | 3 |
| Rules filed for human review (unescapable by rotation) | 2 |
A host of ours gets a filter rule roughly every other day. Not because we're special — because we carry real publisher traffic, and volume is what draws a maintainer's attention. Every one of those 38 was detected, mitigated and redeployed automatically, most within the 15-minute scan interval.
Now the part that bears directly on Trusted Server's design. Three of those events were script-path rotations, and each was triggered by a rule that ignored our domain entirely and matched on the path:
/vkvt4d.js|$script,3p,to=click|space|workers.dev
/itv0eg3.js|$script,3p,to=~com|~net|~org
/8b3ytkn.js|$script,3p,to=~com|~net|~org
Those are real rules, written by real maintainers, in public commits to uBlock Origin's filter repository. We rotate our script path on a schedule because we have watched this happen three times. A randomly generated eight-character filename, used by us alone, got a rule within days of mattering.
/first-party/proxy is not a random eight-character filename. It is a permanent, documented, universal string.
"But it's first-party — lists won't touch it"
This is the strongest argument for Trusted Server's design and it deserves a straight answer rather than a dismissal.
It is partly true. EasyList's stated policy is more lenient toward first-party ad serving, and for a good structural reason: blocking a path on a publisher's own domain risks breaking the publisher's actual site. Maintainers are cautious about collateral damage. A proxy on your own domain genuinely raises the cost of blocking it. We are not going to pretend otherwise.
But leniency is a policy, not a property. And the same lists already write first-party rules when they decide something is worth blocking. In uBlock Origin's own filter list today there are 286 rules carrying the first-party modifier, and they name publisher domains directly:
||cdn.prod.www.spiegel.de/public/spon/generated/web/js/header*.js$script,1p
||handelsblatt.com/*/empty.js$script,1p
||wiwo.de/preparesite/empty.js$script,1p
More to the point, a handful of those rules carry no domain at all — just a path, applied first-party, everywhere:
/ad-m.js$script,1p
/quwet.js$script,1p
/bab.min.js$script,1p
That is precisely the shape of the rule that ends Trusted Server's blocking resistance, and it is one line long. /first-party/proxy$1p would land on every Trusted Server deployment on the internet on the same afternoon, and no individual publisher could do anything about it — because the string is in the spec, not in their config. Add the second-order problem that tsurl= broadcasts doubleclick.net in cleartext, and there are two independent, trivially-matchable signatures on every request.
We checked: there is no such rule today. Not one filter list we scan mentions trusted-server, /first-party/proxy, or tsurl. That is worth saying plainly, and it is worth understanding correctly. Blocking is demand-driven. Nothing gets a rule until it moves enough impressions to be noticed, and Trusted Server is a 0.1.0 project that its own FAQ says isn't ready for production. It is unblocked because it is early, not because it is unblockable. The day it succeeds is the day it gets a rule — and the more publishers adopt it, the more valuable that single line becomes to write.
That is not a criticism of the design. It is what happens to every recovery architecture ever shipped, including ours, repeatedly, on schedule.
The project already knows
We are not the first to notice any of this, and it would be unfair to present it as an outside insight. It is written into the codebase.
The trait every integration implements has an overridable proxy_prefix() method, and the comment explaining why says it exactly:
Override this to provide a custom, customer-specific proxy path that is harder for ad blockers to target.
The Didomi consent integration's public documentation is more explicit still:
By default, Didomi requests are served at /integrations/didomi/consent/*. Since this path is predictable, ad blockers may add it to their block lists. Use proxy_path to set a customer-specific path that is harder to target.
So the argument in this section is not a prediction we're making about someone else's project. It is the project's own stated concern, and it has shipped a fix — for one integration. Didomi is the only one that exposes a configurable proxy_path. GPT's prefix is a hard-coded constant. And /first-party/proxy — the endpoint every rewritten pixel, iframe, creative and stylesheet on the page actually routes through — is a literal in the router with no override at all.
That gap is the whole argument in one sentence. Trusted Server correctly identified that predictable paths get blocked, and then made its most-trafficked path the most predictable thing in the system. Not out of carelessness — out of priorities. Concealment isn't what it's for.
Which is fine, right up until it is the thing standing between a publisher and their revenue.
What you can download, and what you can't
Everything in the first half of this post is free. Apache 2.0, public repo, clone it today. The proxy, the creative rewriter, the six-layer script guard, the identity layer, the redirect handling — the architecture is a solved and published problem, and the IAB deserves real credit for solving it in the open.
What is not in the repository:
The address supply, and someone rotating it. 38 blocked hosts in 76 days is not a code artifact. It is a pool of registered domains, spread across multiple accounts, with a scanner watching three filter repositories every 15 minutes and a mitigation ladder that picks the cheapest escape and redeploys without a human. Trusted Server has exactly one address — yours — and when it gets a rule, the publisher is the one who has to have a plan.
Restraint. Trusted Server proxies for every visitor, always, because signal recovery is its goal and it wants first-party context on all traffic. That is correct for its purpose. For adblock recovery it is the wrong default twice over: you pay a proxy hop for the ~70% of visitors blocking nothing, and you stamp a permanent, always-on signature on every pageview you serve. We install nothing and route nothing until adblocking is confirmed on that specific pageview.
The transparency edge cases. A shim that is almost right is worse than it sounds. The ad renders, the slot fills, and then a vendor-specific check quietly fails — a loading skeleton stays pinned over a paid impression, or refresh never fires. We have shipped fixes for exactly that class of bug, each one found against real vendor code on a live page and locked down with a permanent regression test. Six interception layers is table stakes. Knowing which reads the ad stack actually performs is years of production.
Reporting that survives being blocked. A client script that gets blocked cannot report that it was blocked — the exact failure you most need to see. Our impression, detection and click numbers are recorded server-side, so the dashboard shows blocked pageviews including the ones where our own client never ran.
Operations you don't do. Trusted Server means running a WASM binary on an edge account you own, holding and rotating a proxy_secret, maintaining a 231-line config file, and tracking a 0.1.0 codebase whose current changelog has six breaking changes queued. That is a reasonable trade if your goal is first-party data governance — you probably want that control. It is a strange trade if all you wanted was for your ads to load.
Which one you actually want
These are not really competitors, and it would be dishonest to pretend otherwise.
If your problem is signal loss — third-party cookies gone, IP masking, your first-party data leaking into the bidstream — Trusted Server is aimed squarely at you, it is free, and it is backed by the standards body your partners already follow. Take it seriously.
If your problem is that a third of your audience sees no ads at all, the architecture is necessary but nowhere near sufficient, and the missing half isn't code. It is a supply of addresses and somebody awake to rotate them.
They also compose. A publisher can run Trusted Server for first-party governance and route the blocked cohort through us. Nothing about the two designs conflicts; they were built for adjacent problems by people optimising against different adversaries.
What we would ask you to take from this: the next time someone tells you ad recovery is a gimmick that stops working, the answer is that the IAB Tech Lab convened a task force, took contributions from Fastly and Equativ, spent seventeen months, and shipped the same architecture in public. They validated the design. We operate it.
Every claim about Trusted Server here comes from its public repository and public documentation — commit 2e85a1c, read on 19 August 2026 — and from the IAB Tech Lab's own announcement. Filter-rule counts were taken from the live uBlock Origin and EasyList lists on the same date. The mitigation figures are our own production numbers for 31 May – 15 August 2026. Trusted Server is under fast development and some of these details will be stale by the time you read this; if we have read something wrong, we would genuinely like to hear it.