Skip to content

Stop forwarding ts-ec to static creative assets through /first-party/proxy #1233

Open

Description

@jevansnyc

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-ec to 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_redirects re-applies the parameter per hop.
  • Leave /first-party/click unchanged. 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/proxy does not append ts-ec to 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-fastly and cargo test-axum pass.

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-ec reaching 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

  1. self-assigned this
    on Oct 4, 2026
  2. added theissue type on Oct 4, 2026
  3. aram356 commented on Nov 13, 2026

    @aram356
    Collaborator

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions