Server-side experiments
Server-side experiments, built once you have decided you need them
We implement server-side and edge experiments for teams that have run out of room in the browser: price and promotion tests, logic that must not ship as readable client code, native apps, and headless storefronts with no page to rewrite.
This page assumes the architecture decision is made. If you are still weighing the server against the browser, read the comparison first: it is cheaper reading, and it talks a fair number of teams out of a build they do not need.
- Scoped up front, before a line is written
- The same engineers as the client-side builds
The problem
When server-side is required, and when it is fashion
Server-side testing is the right answer less often than the market implies, and when it is right it is not close. The useful question is whether the thing you want to change exists before the page reaches the browser.
The price is the variable
A price, shipping threshold or promotion rule is computed before anything renders, then carried into the cart, the emails and the order. Rewrite it in the page and all three disagree with what the customer saw.
The logic cannot be readable
Ranking weights, recommendation rules, eligibility thresholds and risk scoring all leak the moment they ship as browser code. Anyone who opens a console reads the experiment.
There is no page to rewrite
A native app, an API-only client, an edge-rendered storefront, or a surface the platform will not let you script. No browser-side tool reaches any of them.
Paint order stopped being winnable
On a heavy or aggressively cached front end there is a point past which no snippet reliably beats first paint. Deciding before the response leaves your infrastructure is the fix, and the only reason here about speed.
If none of those four describes the test in front of you, the browser is still the cheaper place to run it, and we would rather say so than sell you the bigger build.
Deciding whether, versus building it
Two questions get asked about server-side testing, and they want two different pages. If you are still working out whether the architecture is right at all, our comparison of server-side and client-side A/B testing is the one to read first: reach, speed, cost, and what each approach can actually measure.
This page is for once that decision is behind you: what changes in the build, why a feature flag is not yet an experiment, where data has to be passed by hand, which platform constraints bind, and why it costs more. If you get two sections in and suspect you did not need it, that is a good outcome.
What changes in the build
The change is not the language. The decision stops being something a tag does to your page and becomes something your application does while composing a response. The variation is code in your repository, shipped through your release process.
Platforms make this easier than teams expect. Optimizely publishes server-side SDKs for Java, Go, Python, PHP, Ruby and C# among others, and the pattern holds across vendors: the SDK keeps the configuration locally and resolves assignments inside your own process rather than calling out per request.
- Assignment happens once, as early in the request as the framework allows, then travels through rendering, cache keys, the cart and the order.
- Caching becomes part of the design. A cache that does not know a variation exists serves one arm’s HTML to the other, which reads as a broken split rather than a caching bug.
| Part of the test | Client-side build | Server-side build |
|---|---|---|
| Where the variation is decided | In the browser, after the page starts loading | In your application, before the response is composed |
| Who ships it | The testing tool, on its own release cycle | Your repository, through your review and CI |
| What can be changed | Anything already present in the rendered page | Anything the application computes, price and logic included |
| How exposure is recorded | The tool records it while rendering the variation | Your code records it, at the moment of assignment |
| How it is rolled back | Paused in the tool, in minutes | A flag flip if one was designed in, a deploy if not |
A feature flag is not yet an experiment
Almost every server-side experiment rides on a feature flag, and that is where teams stop one step short. A flag serves different code to different people. An experiment is that flag plus a metric chosen before launch, a randomisation unit chosen on purpose, and exposure recorded at assignment.
The vendors say so themselves. LaunchDarkly documents an experiment as a flag with metrics connected to it, and GrowthBook puts the tracking callback inside the SDK while leaving its destination to whoever integrates it. An integration that never wires it anywhere produces a working rollout and no experiment, with nothing on screen to tell the difference.
That is the quiet failure on a stack that adopted flags first. Our article on feature flags versus A/B tests works through the distinction, and is worth reading before this is scoped.
What we build
What the implementation covers
Assignment and delivery
- SDK integration into the application layer you already run, at the earliest point the framework allows
- Cache keys, edge rules and CDN behaviour designed in rather than discovered afterwards
- A default resolving to the control when configuration cannot be fetched or a user is unrecognised
Flags that are actually experiments
- A randomisation unit chosen deliberately, with the constraint it puts on conclusions written down
- Exposure recorded at assignment, carrying the payload the analysis needs, not the SDK default
- Kill switches designed in, so a bad variation is a flag flip and not a late-night deploy
The data chain end to end
- The assignment carried into the cart and the order, so revenue attaches to an arm
- The experiment key present in analytics and the warehouse, so the two can be joined
- Metric definitions agreed before launch, guardrails included, so a winner can be disqualified
Platform-specific work
- Headless and edge-rendered storefronts, Hydrogen on Oxygen and Next.js on your own infrastructure
- Price, shipping-threshold and promotion tests where the number must agree everywhere
- Native app and API client assignment, where an old installed version must behave predictably
The data pass-through, where the work actually is
On a browser-side test the tool sees the page, so attaching the experiment to what happens next is nearly free. On the server nothing is automatic, and four decisions carry the weight.
- Identity comes first and cannot be swapped halfway. A signed-in identifier assigns cleanly across devices but excludes everybody else; an anonymous one covers everyone and splits a person across theirs.
- Exposure fires at assignment, not eligibility. Recording everyone who could have been bucketed dilutes a real effect towards nothing.
- The assignment has to reach the order, because revenue is recorded by your commerce platform, not your experiment tool.
- Analytics needs the same key, or the two can never be joined and you are left with the vendor dashboard.
That chain is worth verifying before launch, the argument our tracking and QA validation page makes for browser-side tests. It matters more here: when a client-side test loses its tracking there is at least a page to inspect.
Platform constraints worth knowing before you scope
On Shopify the word needs qualifying, because a Liquid storefront is rendered by Shopify and not by you, leaving the edge, an app proxy or Shopify Functions. Checkout is its own regime: Shopify documents that checkout.liquid no longer works on the information, shipping or payment steps. A Hydrogen storefront is different, being a React application you deploy to Oxygen or your own infrastructure, a genuine rendering layer. Our guide to testing on Shopify covers what is testable on which plan.
Search is the second constraint, and the server trips it more easily, for an uncomfortable reason: it genuinely can hand a crawler something different. Google’s guidance on website testing asks you to stay out of cloaking, point rel="canonical" at the original across variation URLs, prefer a 302 over a 301, and run an experiment only as long as it needs. All four are cheap at build time and awkward to retrofit.
Mobile applications are the third: an assignment has to survive a copy installed before the experiment existed and not updated for weeks, so the payload a flag returns and its default are part of the test design.
How it runs
How a server-side build is run
Scoped up front rather than started and discovered, because the expensive mistakes here are architectural and made in the first hour.
- 1
Architecture read
Where the request is composed, what caches sit in front of it, what identity exists there, and where the order is written. Sometimes the answer is that this test does not need the server.
- Request path and cache layers mapped
- Identity established at assignment time
- The cheaper alternative named if there is one
- 2
Wire the decision
SDK integration, assignment at the earliest sensible point, and the variation written as code that goes through your review and pipeline like anything else.
- Assignment resolved inside your own process
- Variation code reviewable and revertible
- Control as the default on every failure path
- 3
Wire the measurement
Exposure recorded at assignment, the experiment key carried into the cart and the order, and the same key landed in analytics, so a readout can be rebuilt from your data.
- Exposure fired at assignment, not eligibility
- Arm carried through to the order record
- Analytics and warehouse joinable on one key
- 4
Force every state and verify
Each arm forced deliberately and followed the whole way: rendering, cache behaviour under load, the cart, the order and the event stream. None of it is confirmable by looking at a page.
- Both arms forced and walked end to end
- Cache behaviour checked when warm
- Split ratio watched from day one
Why it costs more, in order
- It is application code, so review, CI, staging and release scheduling all apply. None of that applies to a variation pasted into a tool.
- The first build on a stack pays for everything: SDK integration, the identity decision, exposure plumbing, cache keys. The fifth pays almost none of it.
- Rollback is a deploy unless somebody designed otherwise, so the kill switch belongs in the first scope rather than the second.
None of that argues against the work when a test requires it. It argues against buying it for a headline change, and for sequencing the first build so the setup is paid for by an experiment that could not have run otherwise. If what you want is the whole loop rather than this layer, the A/B test development page is the wider offer and this work sits inside it.
The answers to your questions.
Probably not, unless one of four things is true of the test in front of you. It is required when the thing being changed is computed before any page exists, which covers prices, shipping thresholds and eligibility rules; when the logic would leak commercially as readable browser code; when there is no rendered page to modify at all; or when the front end is heavy enough that paint order can no longer be won from a snippet. For copy or layout, the browser stays cheaper.
Three things, and none is a tool decision. Access to the repository and release process the variation ships through, because this is application code and not a tag. A settled answer on what identity the experiment keys on, since swapping halfway invalidates the run. And a destination for exposure events your analysis reads. Most teams have the first, assume the second and never verified the third.
No: the flag is the delivery mechanism and the experiment is that flag plus the measurement. A flag decides who receives which code. An experiment adds a metric chosen before launch, a randomisation unit chosen on purpose, and exposure recorded at assignment. Vendor documentation frames it the same way, describing experimentation as flags with metrics connected to them. The failure is quiet: flags serving variants for months, and no exposure data to analyse.
Not meaningfully, when implemented the way the platforms intend. Server-side SDKs hold the configuration locally and resolve an assignment inside your own process rather than making a blocking request on every visit, so what you add is application work, not a network round trip. The genuine risk sits elsewhere: a cache key that does not account for the variation. Get that wrong and the cost is not milliseconds, it is one arm being served the other arm’s page.
Yes, though what the word means depends on which Shopify you have. A Liquid storefront is rendered by Shopify rather than by you, so the practical routes are the edge, an app proxy or Shopify Functions, and checkout is a separate regime now that customisation runs through Extensions in Checkout. A Hydrogen storefront is a React application you deploy yourself, which makes this ordinary rather than a workaround. Price testing is what pushes most teams here: a browser-rewritten price disagrees with the cart and the order.
Got a test the browser cannot reach?
Describe the change and the stack it has to run on. We will tell you whether it genuinely needs the server, and what the first build would have to solve.
Describe the test and the stack ↗