Skip to main content

A/B Testing

A/B testing splits traffic between two or more service versions to compare their behavior under real production conditions. Unlike canary deployments (which progressively roll out a new version), A/B testing is about comparison — you run both versions side by side and measure which performs better before making a decision.

Traefik Hub API Gateway supports A/B testing through Weighted Round Robin (WRR) for random traffic splitting, and multi-layer routing combined with identity-based routing for targeting specific user cohorts.


Random Traffic Split

The simplest A/B test sends a fixed percentage of traffic to each version. WRR distributes requests proportionally based on configured weights.

Configuration Example

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: checkout
namespace: apps
spec:
entryPoints:
- websecure
routes:
- match: Host(`shop.example.com`) && PathPrefix(`/checkout`)
kind: Rule
services:
- name: checkout-ab
namespace: apps
kind: TraefikService
tls: {}

Sticky Sessions for Consistent A/B Experience

In an A/B test, a user who starts on version A should stay on version A for the duration of their session. Without stickiness, a user might see version A on one page load and version B on the next, which corrupts your test data and creates a confusing experience.

Configuration Example

Enable cookie-based stickiness on the WRR service:

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: checkout-ab-sticky
namespace: apps
spec:
weighted:
sticky:
cookie:
name: ab_cohort
secure: true
httpOnly: true
services:
- name: checkout-v1
port: 80
weight: 50
- name: checkout-v2
port: 80
weight: 50

On the first request, Traefik assigns the user to a cohort via a Set-Cookie header. Subsequent requests with that cookie are routed to the same version. The cookie name (ab_cohort) can also be read by your analytics pipeline to correlate metrics with the test variant.

For situations where cookies aren't an option — API clients, mobile apps, or sessionless connections — Traefik also supports Highest Random Weight (HRW) hashing, which deterministically routes requests to the same backend based on attributes like the client's IP address. Same consistency guarantee, no cookies required.

Stickiness & Unhealthy Servers

If the server assigned by the sticky cookie becomes unhealthy, Traefik automatically routes the request to another available server and updates the cookie.

If cookies are not feasible (API clients, mobile apps, or sessionless connections), use Highest Random Weight (HRW) for server-side stickiness based on the client's IP address.

Identity-Based A/B Testing

Instead of splitting traffic randomly, you can target specific user cohorts based on identity claims. This uses multi-layer routing to authenticate the request first, then route based on the user's attributes.

This approach is useful when you want to:

  • Test a new checkout flow with internal employees only
  • Run the A/B test exclusively for users in a specific region or tier
  • Combine identity targeting with weighted splitting (e.g., 50% of beta users get version B)

Configuration Example

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: checkout-parent
namespace: apps
spec:
entryPoints:
- websecure
routes:
- match: Host(`shop.example.com`) && PathPrefix(`/checkout`)
kind: Rule
middlewares:
- name: jwt-extract-group
tls: {}
Why Multi-Layer Routing?

You can't match on JWT-derived headers in the same router because middleware runs after router matching. Multi-layer routing solves this: the parent router matches broadly, runs the JWT middleware to inject claim headers, and then child routers match on those headers.

Combining Identity Targeting with Weighted Split

You can route a specific cohort to a WRR service for further splitting. For example, send beta testers to a 50/50 split between versions while everyone else stays on version A:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: checkout-ab-routing
namespace: apps
spec:
parentRefs:
- name: checkout-parent
namespace: apps
routes:
- match: HeadersRegexp(`X-User-Group`, `beta-tester`)
kind: Rule
services:
- name: checkout-ab-beta-wrr
namespace: apps
kind: TraefikService
- match: PathPrefix(`/`)
kind: Rule
priority: 0
services:
- name: checkout-v1
port: 80