Skip to main content

Version: 3.10.x

a7-plugin-traffic-split

Overview

The traffic-split plugin dynamically directs portions of traffic to different upstream services based on custom rules (match) and weighted distributions (weighted_upstreams). Use it for canary releases, blue-green deployments, and A/B testing — all without modifying DNS or load balancers.

When to Use

  • Canary release: gradually shift traffic to a new version (10% → 50% → 100%)
  • Blue-green deployment: switch traffic based on request headers or cookies
  • A/B testing: split traffic by user attributes (headers, query params, cookies)
  • Feature flags: route specific users to feature branches
  • Multi-version API: run multiple backend versions simultaneously

Plugin Configuration Reference (Route/Service)

Top-level

FieldTypeRequiredDefaultDescription
rulesarray[object]YesList of traffic splitting rules. Each rule has optional match and required weighted_upstreams.

rules[].match

FieldTypeRequiredDefaultDescription
matcharray[object]No[]Conditions to activate this rule. Empty = unconditional (all traffic uses weights).
match[].varsarray[array]NoVariable expressions: ["variable", "operator", "value"]. Uses Nginx variables. Multiple vars in one object = AND. Multiple objects in match = OR.

Operators: ==, ~=, >, <, >=, <=, ~~ (regex match), !~~, in, has, ! — see lua-resty-expr.

Common variables: arg_name (query param), http_header-name (request header), cookie_name (cookie value).

rules[].weighted_upstreams[]

FieldTypeRequiredDefaultDescription
upstreamobjectNoInline upstream configuration (see below).
weightintegerNo1Traffic weight for this upstream.

If only weight is set (no inline upstream), traffic goes to the route's service upstream.

Inline upstream object

FieldTypeRequiredDefaultDescription
typestringNo"roundrobin"Load balancing: "roundrobin" or "chash".
nodesarrayYesBackend nodes as [{host, port, weight}].
timeoutobjectNo15 (seconds){"connect": N, "send": N, "read": N}
pass_hoststringNo"pass""pass" = client host, "node" = upstream node, "rewrite" = use upstream_host.
upstream_hoststringNoCustom Host header. Only works with pass_host: "rewrite".
namestringNoHuman-readable name for the upstream.

Not supported in inline weighted upstreams: service_name, discovery_type, checks, retries, and retry_timeout. Keep shared upstream behavior on the bound service whenever possible.

Step-by-Step: Enable traffic-split on a Route

1. Canary release — 20% to new version

Configure a 20/80 split for gateway group default:

a7 service create --gateway-group default -f - <<'EOF'
{
"id": "stable-api-service",
"name": "stable-api-service",
"upstream": {
"type": "roundrobin",
"nodes": [{"host": "backend-v1", "port": 8080, "weight": 1}]
}
}
EOF

a7 route create --gateway-group default -f - <<'EOF'
{
"id": "canary-release",
"uri": "/api/*",
"service_id": "stable-api-service",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"name": "new-version-v2",
"type": "roundrobin",
"nodes": [{"host": "backend-v2", "port": 8080, "weight": 1}]
},
"weight": 2
},
{
"weight": 8
}
]
}
]
}
}
}
EOF

Result: 20% traffic → backend-v2, 80% → backend-v1 (route default).

2. Blue-green deployment — header-based switching

a7 service create --gateway-group prod -f - <<'EOF'
{
"id": "blue-api-service",
"name": "blue-api-service",
"upstream": {
"type": "roundrobin",
"nodes": [{"host": "blue-backend", "port": 8080, "weight": 1}]
}
}
EOF

a7 route create --gateway-group prod -f - <<'EOF'
{
"id": "blue-green",
"uri": "/api/*",
"service_id": "blue-api-service",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["http_x-canary", "==", "true"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"name": "green-env",
"type": "roundrobin",
"nodes": [{"host": "green-backend", "port": 8080, "weight": 1}]
},
"weight": 1
}
]
}
]
}
}
}
EOF

Result: Requests with header x-canary: true → green, all others → blue.

3. Increase canary to 50%

a7 route update canary-release --gateway-group default -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"name": "new-version-v2",
"type": "roundrobin",
"nodes": [{"host": "backend-v2", "port": 8080, "weight": 1}]
},
"weight": 5
},
{
"weight": 5
}
]
}
]
}
}
}
EOF

Common Patterns

A/B testing by query parameter

{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["arg_variant", "==", "B"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"name": "variant-B",
"type": "roundrobin",
"nodes": [{"host": "variant-b", "port": 8080, "weight": 1}]
}
}
]
}
]
}
}
}

Requests with ?variant=B → variant B backend.

Multi-rule routing (OR logic)

{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [{"vars": [["http_x-api-id", "==", "1"]]}],
"weighted_upstreams": [
{"upstream": {"type": "roundrobin", "nodes": [{"host": "svc-a", "port": 8080, "weight": 1}]}}
]
},
{
"match": [{"vars": [["http_x-api-id", "==", "2"]]}],
"weighted_upstreams": [
{"upstream": {"type": "roundrobin", "nodes": [{"host": "svc-b", "port": 8080, "weight": 1}]}}
]
}
]
}
}
}

Health Check Considerations

{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {"type": "roundrobin", "nodes": [{"host": "canary", "port": 8080, "weight": 1}]},
"weight": 2
},
{
"weight": 8
}
]
}
]
}
}
}

Current API7 EE does not expose standalone upstream CRUD through a7. Prefer inline weighted upstreams for traffic splitting, and keep health-check settings on service upstream configuration when needed.

Match Logic Reference

StructureLogic
Multiple entries in one vars arrayAND — all must match
Multiple objects in match arrayOR — any can match
Empty match or no matchUnconditional — always applies weights

Troubleshooting

SymptomCauseFix
Traffic ratio inaccurateRound-robin algorithm causes slight deviationExpected behavior; ratios converge over many requests
Match rule not triggeringVariable name wrong or operator mismatchUse http_header-name for headers, arg_name for query params
Health checks not workingHealth checks are not configured on the service upstreamMove stable backend health-check settings to the service upstream configuration
All traffic going to defaultMatch conditions never trueDebug with a7 route get and verify header/param names
Weight 0 not blocking trafficWeight 0 means "never forward" to that upstreamCorrect — set weight to 0 to exclude an upstream
Config not appliedWrong gateway group specifiedEnsure --gateway-group matches the desired cluster

Config Sync Example

version: "1"
services:
- id: stable-api-service
name: stable-api-service
upstream:
type: roundrobin
nodes:
- host: stable-backend
port: 8080
weight: 1
routes:
- id: canary-api
uri: /api/*
service_id: stable-api-service
plugins:
traffic-split:
rules:
- weighted_upstreams:
- upstream:
type: roundrobin
nodes:
- host: canary-backend
port: 8080
weight: 1
weight: 2
- weight: 8

This page is generated from a7-plugin-traffic-split/SKILL.md in the api7/a7 repository. Browse all skills on the AI Agent Skills page.

API7.ai Logo

The digital world is connected by APIs,
API7.ai exists to make APIs more efficient, reliable, and secure.

Sign up for API7 newsletter

Product

API7 Gateway

SOC2 Type IIISO 27001HIPAAGDPRRed Herring

Copyright © APISEVEN PTE. LTD 2019 – 2026. Apache, Apache APISIX, APISIX, and associated open source project names are trademarks of the Apache Software Foundation