When you deploy multiple versions of a service, you often need to direct specific requests to specific versions -- for example, routing beta users to a canary release or splitting traffic for A/B testing. Header-based routing in Alibaba Cloud Service Mesh (ASM) solves this by inspecting custom HTTP request headers and forwarding traffic to the matching service version.
How it works
A VirtualService defines routing rules that inspect incoming request headers and forward traffic to matching service subsets. Each rule specifies a header name, a match condition, and a destination.
Rules are evaluated top to bottom. The first match wins. Always add a default route as the last rule to catch unmatched requests -- without one, requests that match no rule have no guaranteed destination.
Supported match types
ASM supports three match types for header values, defined in the Istio StringMatch specification:
| Match type | Syntax | Behavior | Example |
|---|---|---|---|
exact | exact: v1 | Case-sensitive exact match | Matches v1, not V1 or v1-beta |
prefix | prefix: v1 | Case-sensitive prefix match | Matches v1, v1-beta, v1.2 |
regex | regex: "v[0-9]+" | RE2 regex match | Matches v1, v2, v123 |
Multiple conditions
You can combine multiple header conditions using AND or OR logic:
AND: Place multiple header conditions inside the same
matchentry. All conditions must be true.OR: Add separate entries to the
matcharray. Any single entry triggers the route.
# AND: both headers must match (multiple conditions in one match block)
http:
- match:
- headers:
env:
exact: staging
user-group:
exact: beta
route:
- destination:
host: myapp
subset: v2
# OR: either header triggers the route (separate match blocks)
http:
- match:
- headers:
env:
exact: staging
- headers:
user-group:
exact: beta
route:
- destination:
host: myapp
subset: v2Prerequisites
Before you begin, make sure that you have completed the steps in Preparations to deploy the helloworld and sleep sample services.
Verify the default routing behavior
Before adding header-based rules, confirm that the helloworld service responds randomly from both versions.
Open a shell in the
sleeppod:kubectl exec -it deploy/sleep -- shSend a request to the
helloworldservice.curl helloworld:5000/helloEither
helloworld-v1orhelloworld-v2responds at random:Hello version: v2, instance: helloworld-v2-6b96c5684-4**** Hello version: v1, instance: helloworld-v1-6d77f4c4cf-p****
Create a destination rule
Define subsets for each version of the helloworld service. Subsets group endpoints by the version label so that routing rules can target specific versions.
Apply the following YAML. For details, see Manage destination rules.
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: helloworld
namespace: default
spec:
host: helloworld
subsets:
- labels:
version: v1
name: v1
- labels:
version: v2
name: v2Key fields:
| Field | Description |
|---|---|
host | The Kubernetes service name that this rule applies to. |
subsets[].name | A label for the subset. Routing rules reference this name. |
subsets[].labels | The pod labels used to select endpoints. Here, v1 selects pods with version: v1, and v2 selects pods with version: v2. |
Create a virtual service with header matching
Create a VirtualService that routes traffic based on the test-header request header. For details, see Manage virtual services.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: helloworld-vs
namespace: default
spec:
hosts:
- helloworld
http:
- match:
- headers:
test-header:
exact: v1
route:
- destination:
host: helloworld
subset: v1
- match:
- headers:
test-header:
exact: v2
route:
- destination:
host: helloworld
subset: v2Key fields:
| Field | Description |
|---|---|
hosts | The service this VirtualService applies to. |
http[].match[].headers | The header name and match condition. Here, test-header is matched with exact, so the value must be an exact, case-sensitive match. |
http[].route[].destination.subset | The target subset defined in the destination rule. |
Verify header-based routing
Open a shell in the
sleeppod:kubectl exec -it deploy/sleep -- shSend a request with
test-header: v1.curl -H "test-header:v1" helloworld:5000/helloExpected output:
Hello version: v1, instance: helloworld-v1-6d77f4c4cf-p****Send a request with
test-header: v2.curl -H "test-header:v2" helloworld:5000/helloExpected output:
Hello version: v2, instance: helloworld-v2-6b96c5684-4****
What's next
Use
prefixorregexmatching for more flexible routing. For example, useprefix: canaryto route all requests where the header value starts withcanaryto a canary subset.Combine header matching with weight-based routing for gradual traffic shifts.
See Manage virtual services for the full VirtualService configuration reference.