Repository navigation
Description
Description
/first-party/proxy appends the reader's EC id as a ts-ec query parameter to every upstream URL it fetches, whenever the ts-ec cookie is present. The handler hardcodes forward_ec_id: true (crates/trusted-server-core/src/proxy.rs, handle_first_party_proxy), and append_ec_id upserts the parameter on the target URL and again on each redirect hop.
That is the right behavior for endpoints that are meant to receive identity (impression pixels, win notices, click redirects). It is the wrong behavior for static creative assets, which are the majority of what the creative rewriter sends through the proxy: the 728x90 image, fonts, the viewability script, CSS, the HTML of an iframe creative. For those URLs the parameter has two costs.
Cache defeat. On Fastly the backend fetch goes through the platform cache unless TS sets pass, and TS only sets pass on the publisher HTML fetch. The cache key includes the backend URL, so https://cdn.example/728x90.jpg?ts-ec=<reader A> and https://cdn.example/728x90.jpg?ts-ec=<reader B> are different objects. Every reader triggers a fresh fetch of the same asset from the creative CDN, and the CDN's own Cache-Control never helps across readers. The browser-facing URL is already stable across readers (the tstoken is a deterministic hash of the target URL with no per-user input), so only the upstream side is affected.
Identity exposure. The EC id is handed to every creative CDN host and script host the creative references, not only to the vendors the operator intended to receive it. Hosts that serve static assets gain a per-reader identifier they have no use for and no agreement to receive.
Precedent already exists for turning forwarding off per path: the JS asset proxy and the Prebid bundle proxy both call .without_ec_id().
Proposal
Make EC forwarding on /first-party/proxy selective rather than unconditional.
- Default: do not append
ts-ecto upstream fetches. - Forward only when the target matches an operator-configured allowlist of identity endpoints (URL host and path patterns under
[proxy]), or, if a default-on posture is preferred, provide a denylist by asset type (image, font, script, stylesheet extensions and known static CDN hosts) with the allowlist taking precedence. - Apply the same rule on every redirect hop, since
proxy_with_redirectsre-applies the parameter per hop. - Leave
/first-party/clickunchanged. Forwarding to the advertiser landing page is intentional there and is a navigation, not a cacheable fetch. - Document the setting and its default in
docs/guide/first-party-proxy.md, which currently states that the parameter is appended automatically.
Classification has to be by URL, not by response content type, because the decision is made before the fetch. HTML creatives fetched through the proxy are rewritten again on the way back, so trackers inside them get their own signed proxy URLs and are classified on their own fetch.
Done when
/first-party/proxydoes not appendts-ecto a target unless the target matches the configured forwarding rule.- The rule is applied on the initial target and on every redirect hop.
- Two readers with different EC cookies requesting the same signed proxy URL for a static asset produce the same upstream URL, verified by a unit test on the built target URL and by a Viceroy test observing a single origin fetch for the second request.
- Impression pixels and other allowlisted endpoints still receive
ts-ec, verified by test. - Default configuration (no rule set) is documented, and the first-party proxy guide no longer says the parameter is always appended.
cargo test-fastlyandcargo test-axumpass.
Open questions
- Default-off with an allowlist, or default-on with a denylist. Default-off is the safer privacy posture and matches how the JS asset proxy already behaves. Default-on preserves today's behavior for operators who rely on
ts-ecreaching trackers without configuration. - Whether the rule should also gate the curated client-derived headers forwarded by the proxy, which raise the same cache-key question for assets.
Affected area
Core (Edge Cookies, GDPR); first-party proxy in crates/trusted-server-core/src/proxy.rs.
Activity
aram356 commentedon Nov 13, 2026
CREATOR: This issue is now part of #1558, server-side identity on every adapter. It is planned in wave 0 (MVP: Fastly and Axum); priority P2, size S.
Blocked by: nothing.
Design: spec; order and dependencies: plan.