You may prefer not to load third-party experimentation scripts directly from vendor-hosted domains or want to perform client-side experiment evaluation. There could be a variety of reasons to not want this including security policies, privacy controls, or tighter infrastructure ownership.
Proxy-based experimentation architecture lets teams retain full control over script delivery, evaluation, and exposure tracking while using a centralized experimentation platform.
This document outlines common proxy patterns and the key implementation details required to support them.
Customers host a lightweight endpoint that proxies requests for the experimentation JavaScript SDK. End users load the script from a customer-owned domain rather than a third-party domain.
This lets you:
https://cdn.amplitude.com/script/{API_KEY}.experiment.js → /script/{API_KEY}.experiment.jsAPI_KEYBefore (direct to Amplitude):
<script src="https://cdn.amplitude.com/script/abc123def456.experiment.js"></script>
After (through your proxy):
<script src="https://experiments.acme.com/script/abc123def456.experiment.js"></script>
The Web Experiment script calls a single endpoint for remote evaluation. An experiment requires remote evaluation when its targeting rules contain at least one property that is not available locally on the client.
This lets you:
Create and expose an HTTPS endpoint for remote evaluation for the Web Experiment SDK:
Example URLs:
https://experiments.acme.com/sdk/v2/flags?delivery_method=webhttps://experiments.acme.com/The SDK uses this base URL for all evaluation requests.
Configure the Web Experiment script/SDK to call your new endpoint instead of the Amplitude-hosted URLs (flag.lab.amplitude.com or flag.lab.eu.amplitude.com). Enter the following code above the Web Experiment script.
<script>
window.experimentConfig = {
flagsServerUrl: 'https://experiments.acme.com/',
}
</script>
For each incoming browser request, the proxy must:
Accept request
delivery_method=webForward to Amplitude
https://flag.lab.amplitude.com/sdk/v2/flags?delivery_method=webhttps://flag.lab.eu.amplitude.com/sdk/v2/flags?delivery_method=webInject Headers
| Header Name | Value | Notes |
|---|---|---|
| Authorization | Api-Key <AMPLITUDE_PROJECT_API_KEY> |
Stored and managed only on the server. Never exposed to the browser. |
| X-Amp-Exp-User | <base64-encoded user object> |
Forward from browser as-is. Contains {"user_id": "...", "device_id": "..."} |
Return Response: Return Amplitude’s JSON response unchanged to the browser.
Browser to Proxy:
GET https://experiments.acme.com/sdk/v2/flags?delivery_method=web
X-Amp-Exp-User: eyJ1c2VyX2lkIjoidXNlciIsImRldmljZV9pZCI6ImRldmljZSJ9
Proxy to Amplitude:
GET https://flag.lab.amplitude.com/sdk/v2/flags?delivery_method=web
Authorization: Api-Key <PROJECT_API_KEY>
X-Amp-Exp-User: eyJ1c2VyX2lkIjoidXNlciIsImRldmljZV9pZCI6ImRldmljZSJ9
Proxy to Browser: Returns Amplitude's evaluation response.
The endpoint returns a JSON array of experiment flag objects pre-evaluated for the user.
Flag object schema:
| Field | Description |
|---|---|
| key | Unique flag key identifying the experiment. |
| segments | Targeting and bucketing rules pre-evaluated with the user’s properties. Conditions that are true are included, others are removed. |
| variants | Available variants including: key, metadata, and web experiment actions (mutations, custom code, URL redirects). |
| metadata | Experiment metadata: version, deployment status, and re-run indicators. |
[
{
"key": "example",
"metadata": {
"deliveryMethod": "web",
"deployed": true,
"evaluationMode": "local",
"experimentKey": "exp-1",
"exposureEvent": "$impression",
"flagType": "experiment",
"flagVersion": 5
},
"segments": [
{
"bucket": {
"allocations": [
{
"distributions": [
{ "range": [0, 21474837], "variant": "control" },
{ "range": [21474836, 42949673], "variant": "treatment" }
],
"range": [0, 100]
}
],
"salt": "G0U0BTSK",
"selector": ["context", "user", "web_exp_id"]
},
"conditions": [
[
{ "op": "is", "selector": ["context", "user", "device_category"], "values": ["desktop"] }
]
],
"metadata": { "segmentName": "Segment 1" },
"variant": "off"
},
{
"metadata": { "segmentName": "All Other Users" },
"variant": "off"
}
],
"variants": {
"control": {
"key": "control",
"payload": [{ "action": "mutate", "data": { "mutations": [] } }],
"value": "control"
},
"off": { "key": "off", "metadata": { "default": true } },
"treatment": {
"key": "treatment",
"payload": [
{
"action": "mutate",
"data": {
"mutations": [
{
"action": "set",
"attribute": "html",
"metadata": { "scope": ["89e01141-6e35-4bc0-bb66-0e1bc0fd4823"], "type": "text" },
"selector": ".md\\:max-w-max:nth-child(1)",
"value": "updated text"
}
]
}
}
],
"value": "treatment"
}
}
}
]
Impression (exposure) events are sent from customer-controlled backend systems to the experimentation platform rather than directly from the browser.
This lets you:
If you already use Amplitude's Analytics SDK, update the tracking endpoint for the browser SDK through serverUrl.
If you are not using Amplitude's Analytics SDK, you can add the following script before the Web Experiment script.
Important: The impression event forwarding script must be placed above the Web Experiment script.
<script>
// TODO: Replace this object with your tracker implementation.
const customTracker = {
track: (eventType, eventProperties) => {
// TODO: Send eventType and eventProperties to your analytics tool.
console.log({ eventType, eventProperties });
},
};
window.experimentIntegration = {
getUser: () => {
// TODO: Return user.
return {
user_id: "user",
device_id: "device",
};
},
track: (e) => {
// TODO: Track event
customTracker.track(e.eventType, e.eventProperties);
return true;
},
};
</script>
December 23rd, 2024
Need help? Contact Support
Visit Amplitude.com
Have a look at the Amplitude Blog
Learn more at Amplitude Academy
© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.