First-Party Proxy
Learn how Trusted Server proxies third-party assets through first-party domains to improve privacy, security, and ad performance.
Overview
The First-Party Proxy system rewrites third-party URLs in ad creatives to route through your domain, providing:
- Privacy Protection - No direct third-party cookies or tracking
- Synthetic ID Forwarding - Controlled identity propagation
- Creative Rewrites - Automatic HTML/CSS URL transformation
- Click Tracking - First-party click redirects
- Content Security - Validated, signed URLs prevent tampering
How It Works
Core Endpoints
/first-party/proxy - Asset Proxy
Proxies third-party assets with automatic HTML/CSS rewriting.
Request:
GET /first-party/proxy?tsurl=https://example.com/ad.html&tstoken=signatureQuery Parameters:
| Parameter | Required | Description |
|---|---|---|
tsurl | Yes | Base URL of the resource (without query params) |
tstoken | Yes | HMAC-SHA256 signature of the full URL |
tsexp | No | Unix timestamp expiration (30s default from signing) |
| (others) | No | Original query parameters from target URL |
Behavior:
- Validates the
tstokensignature against reconstructed URL - Appends
synthetic_idquery parameter (if available) - Proxies request to target URL with forwarded headers:
User-AgentAcceptAccept-LanguageAccept-EncodingRefererX-Forwarded-For
- Processes response based on content type:
- HTML (
text/html) - Rewrites all URLs, returnstext/html - CSS (
text/css) - Rewritesurl()values, returnstext/css - Images - Detects pixels, sets
image/*if missing - Other - Passthrough without modification
- HTML (
Example:
Original URL:
https://tracker.com/pixel.gif?campaign=123&uid=abcSigned proxy URL:
/first-party/proxy?
tsurl=https://tracker.com/pixel.gif&
campaign=123&
uid=abc&
tstoken=HmacSha256SignatureFinal proxied request:
https://tracker.com/pixel.gif?campaign=123&uid=abc&synthetic_id=xyz/first-party/click - Click Redirects
Handles click tracking with first-party redirects.
Request:
GET /first-party/click?tsurl=https://advertiser.com/landing&tstoken=signatureQuery Parameters: Same as /first-party/proxy
Behavior:
- Validates the
tstokensignature - Appends
synthetic_idparameter to target URL - Issues 302 redirect to target (browser navigates directly)
- Logs click metadata:
- Target URL base (
tsurl) - Whether parameters were present
- Full reconstructed URL
- Referer, User-Agent
- Synthetic ID (if available)
- Target URL base (
Example:
Click URL in creative:
<a
href="/first-party/click?
tsurl=https://advertiser.com/buy&
product=widget&
tstoken=signature"
>
Buy Now
</a>User clicks → Server responds:
HTTP/1.1 302 Found
Location: https://advertiser.com/buy?product=widget&synthetic_id=xyzClick vs Proxy
Use /first-party/click for navigational links (anchors) since it avoids downloading content. Use /first-party/proxy for embedded resources (images, scripts, iframes) that need to be rendered inline.
/first-party/sign - URL Signing
Generates signed proxy URLs for dynamic use cases.
Request (GET):
GET /first-party/sign?url=https://example.com/resource.jpgRequest (POST):
POST /first-party/sign
{
"url": "https://example.com/resource.jpg?param=value"
}Response:
{
"href": "/first-party/proxy?tsurl=https://example.com/resource.jpg¶m=value&tstoken=signature&tsexp=1234567890",
"base": "https://example.com/resource.jpg"
}Response Fields:
| Field | Description |
|---|---|
href | Complete signed proxy URL ready to use |
base | Original base URL (without query parameters) |
Expiration:
- Default: 30 seconds from signing
tsexpparameter included in signed URL- Validation fails after expiration
Example Usage:
// Client-side JavaScript dynamically signing URLs
fetch('/first-party/sign?url=' + encodeURIComponent(imageUrl))
.then((r) => r.json())
.then((data) => {
img.src = data.href // Use signed URL
})/first-party/proxy-rebuild - URL Modification
Modifies existing signed URLs by adding or removing parameters.
Request:
POST /first-party/proxy-rebuild?tsclick=encoded_click_url&add=key:value&del=keyQuery Parameters:
| Parameter | Required | Description |
|---|---|---|
tsclick | Yes | Base64-encoded original click/proxy URL |
add | No | Parameter to add (format: key:value) |
del | No | Parameter key to remove |
Behavior:
- Decodes the
tsclickbase64 URL - Parses existing parameters
- Adds specified parameters (
add) - Removes specified parameters (
del) - Re-signs the modified URL
- Returns new signed URL
Example:
Original click URL:
/first-party/click?tsurl=https://example.com&product=A&tstoken=sig1Modify URL (add variant=red, remove product):
POST /first-party/proxy-rebuild?
tsclick=L2ZpcnN0LXBhcnR5L2NsaWNrP3RzdXJsPWh0dHBzOi8vZXhhbXBsZS5jb20mcHJvZHVjdD1BJnRzdG9rZW49c2lnMQ==&
add=variant:red&
del=productResponse:
{
"href": "/first-party/click?tsurl=https://example.com&variant=red&tstoken=sig2"
}Use Cases
This endpoint is designed for advanced scenarios like A/B testing where you need to modify URLs without re-signing from scratch. Most implementations won't need this.
URL Signing & Validation
Trusted Server signs proxy and click URLs using the publisher proxy_secret. Signed URLs include a tstoken and may include tsexp for expiration.
For the detailed signing algorithm, validation steps, and security notes, see Proxy Signing.
Content Type Handling
HTML Rewriting
Triggers: Response Content-Type: text/html
Process:
- Parse HTML with streaming processor
- Rewrite absolute URLs to
/first-party/proxyor/first-party/click - Preserve relative URLs unchanged
- Sign all rewritten URLs with
tstoken - Return as
text/html; charset=utf-8
Rewritten Elements: See Creative Processing for full list.
CSS Rewriting
Triggers: Response Content-Type: text/css
Process:
- Parse CSS for
url(...)values - Rewrite absolute URLs to
/first-party/proxy - Sign URLs with
tstoken - Return as
text/css; charset=utf-8
Example:
/* Original */
.banner {
background: url(https://cdn.com/bg.jpg);
}
/* Rewritten */
.banner {
background: url(/first-party/proxy?tsurl=https://cdn.com/bg.jpg&tstoken=sig);
}Image Handling
Triggers:
- Response
Content-Type: image/*, OR - Request
Acceptheader containsimage/
Process:
- Set
Content-Type: image/*if missing - Detect likely pixels with heuristics:
Content-Length≤ 256 bytes- URL contains
/pixel,/p.gif,/1x1,/track
- Log pixel detection (no response alteration)
- Passthrough image data
Logging:
proxy: likely pixel detected size=43 url=https://tracker.com/p.gifPassthrough (Other Types)
Triggers: Any other Content-Type
Process:
- Forward response without modification
- Preserve original
Content-Type - No HTML/CSS/URL rewriting
- Useful for: JSON, JavaScript, binary files, etc.
Redirect Handling
The proxy automatically follows HTTP redirects:
Supported Status Codes:
301- Moved Permanently302- Found303- See Other (switches to GET)307- Temporary Redirect308- Permanent Redirect
Behavior:
- Follow up to 4 redirect hops
- Re-apply
synthetic_idon each hop - Switch to
GETafter303response - Log when redirect limit reached
- Preserve request headers across hops
Example Flow:
Request: /first-party/proxy?tsurl=https://short.link&tstoken=sig
→ 302 to https://cdn.com/ad.html
→ 200 with HTML content
→ Rewrite HTML and returnSynthetic ID Propagation
Automatic Forwarding
When proxying, Trusted Server automatically appends the synthetic_id parameter:
Source Priority:
x-synthetic-idrequest headersynthetic_idcookie- Generate new ID if missing
Example:
Original request to proxy:
/first-party/proxy?tsurl=https://tracker.com/pixel.gif&tstoken=sig
Cookie: synthetic_id=user123
Proxied backend request:
https://tracker.com/pixel.gif?synthetic_id=user123Redirect Propagation
Synthetic IDs are re-applied on every redirect hop:
/first-party/proxy?tsurl=https://redirect1.com&tstoken=sig
→ https://redirect1.com?synthetic_id=user123
→ 302 to https://redirect2.com
→ https://redirect2.com?synthetic_id=user123
→ 302 to https://final.com
→ https://final.com?synthetic_id=user123
→ 200 responseThis ensures downstream trackers receive consistent IDs even through redirect chains.
Click ID Forwarding
Click redirects also forward synthetic IDs:
<a href="/first-party/click?tsurl=https://advertiser.com&tstoken=sig"></a>User clicks → redirect includes ID:
302 Found
Location: https://advertiser.com?synthetic_id=user123Privacy Control
Synthetic IDs are only forwarded when:
- User has given GDPR consent (if required)
- ID exists in request (header/cookie)
- Integration hasn't disabled forwarding (
forward_synthetic_id: false)
Configuration
Publisher Settings
Configure proxy behavior in trusted-server.toml:
[publisher]
domain = "publisher.com"
origin_url = "https://origin.publisher.com"
proxy_secret = "your-secure-random-secret"
cookie_domain = ".publisher.com" # For synthetic_id cookiesURL Rewrite Exclusions
Exclude specific domains from rewriting:
[rewrite]
exclude_domains = [
"*.cdn.trusted.com", # Wildcard pattern
"first-party.example.com" # Exact match
]URLs matching these patterns will NOT be rewritten to /first-party/proxy.
Streaming vs Buffered
Control whether responses are streamed or buffered:
// Integration code example
ProxyRequestConfig::new(url)
.with_streaming() // Enable streaming (no HTML/CSS rewrites)Streaming (buffered rewrites disabled):
- Preserves origin compression (gzip/brotli)
- Lower memory usage
- No HTML/CSS URL rewriting
- Best for: large files, images, videos
Buffered (default):
- Enables HTML/CSS rewriting
- Decompresses response
- Higher memory usage
- Best for: ad creatives, landing pages
Performance Optimization
Compression
Buffered Mode:
- Decompresses origin response
- Processes content
- Returns uncompressed (Fastly can re-compress)
Streaming Mode:
- Preserves origin
Content-Encoding - No decompression/recompression overhead
- Passes through gzip/brotli/deflate
Caching
Proxy responses respect origin cache headers:
Cache-ControlExpiresETagLast-Modified
Best Practices:
Cache-Control: public, max-age=3600
Vary: Accept-EncodingHeader Forwarding
Only essential headers are forwarded to reduce overhead:
Forwarded Headers:
User-Agent- Client identificationAccept- Content negotiationAccept-Language- Language preferencesAccept-Encoding- Compression supportReferer- Page contextX-Forwarded-For- Client IP chain
Not Forwarded:
- Authentication headers (unless explicitly added)
- Cookies (except synthetic ID appended as query param)
- Custom headers (unless added via
ProxyRequestConfig)
Error Handling
Common Errors
Invalid Signature:
HTTP 403 Forbidden
tstoken validation failed: signature mismatchSolutions:
- Verify
proxy_secretmatches signing configuration - Check URL reconstruction includes all parameters in correct order
- Ensure no URL encoding issues
Expired URL:
HTTP 403 Forbidden
tstoken expiredSolutions:
- URLs signed with
/first-party/signexpire in 30s - Re-sign URL if needed
- Check client/server clock sync
Missing Parameters:
HTTP 400 Bad Request
Missing required parameter: tsurlSolutions:
- Ensure
tsurlparameter is present - Include
tstokenin request - Verify URL encoding is correct
Redirect Limit
When redirect limit (4 hops) is reached:
Log: proxy: redirect limit reached for url=https://...Response: Returns last redirect response (302/307/308) without following.
Solutions:
- Contact origin to reduce redirect chain
- Increase limit in code (modify
MAX_REDIRECTSconstant)
Security Considerations
Token Security
Do: ✅ Use cryptographically strong proxy_secret (32+ bytes random)
✅ Rotate secrets periodically
✅ Validate expiration on all requests
✅ Use constant-time comparison for signatures
Don't: ❌ Expose proxy_secret in client-side code
❌ Reuse secrets across environments
❌ Accept unsigned URLs
❌ Skip validation for "trusted" domains
URL Injection Prevention
Signed URLs prevent injection attacks:
Attacker tries:
/first-party/proxy?tsurl=https://evil.com&tstoken=forged
Trusted Server:
1. Computes expected token for https://evil.com
2. Compares with provided token
3. Rejects if mismatch (403 Forbidden)Content Security
Automatic Protection:
- HTML/CSS rewriting removes malicious URLs
- Data URIs are skipped (
data:,javascript:,blob:) - Protocol validation (only
http://andhttps://)
Considerations:
- Origin content is still served (validate trusted sources)
- Streaming mode bypasses HTML inspection
- Enable CSP headers for additional protection
Monitoring & Debugging
Logging
Proxy requests emit detailed logs:
proxy: origin response status=200 ct=text/html cl=1234 accept=text/html url=https://...
proxy: likely pixel detected size=43 url=https://tracker.com/p.gif
click: tsurl=https://advertiser.com had_params=true target=... referer=... ua=... tsid=...Diagnostic Headers
Add custom headers for debugging:
[response_headers]
X-Proxy-Mode = "rewrite"
X-TS-Version = "1.0"Metrics to Track
Key Metrics:
- Proxy request count (total)
- Signature validation failures (rate)
- Redirect hops (average/max)
- Response time (p50/p95/p99)
- Content type distribution
- Pixel detection rate
Next Steps
- Learn about Creative Processing for HTML rewriting details
- Review Synthetic IDs for identity management
- Set up Configuration for your deployment
- Explore Integration Guide for custom integrations