Prerequisites
Please use a Service Mesh such as Istio that implements GEP-1294: xRoutes Mesh Binding.
Introduction
Without using Shadow Service, you can extend existing HTTPRoute without touching a single line of YAML using only Gateway API standard features. The key point is the specification that allows you to specify a Kubernetes Service in the parentRefs of HTTPRoute (GEP-1294).
According to GEP-1294's Route presence, when you create an xRoute with a Service as the parentRef, the implicit delivery (normal Service→Endpoints routing) for that Service is not "added" but can be replaced by the Route. Furthermore, Namespace boundaries states that a producer route with a Service in the same Namespace as parentRef SHOULD be applied to all inbound requests to that Service.
Benefits
- You can customize even when existing tools don't provide fine-grained HTTPRoute customization
- Can be achieved using only Gateway API specifications
Illustrated Diagram
Show Mermaid
flowchart LR
classDef parent stroke-dasharray: 4 2;
classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;
subgraph Ingress
GW[Gateway]
end
subgraph app-ns
HR_a["HTTPRoute a (existing)"]
SVC_a[(Service a)]
DEP_a[Deployment a]
end
subgraph ext-ns
HR_b["HTTPRoute b (new)"]
SVC_b[(Service b)]
DEP_b[Deployment b]
end
GW e1@--> HR_a
HR_a -.parent ref: registration.-> GW
HR_a e2@--> SVC_a
SVC_a -."selector: Note 1".-x DEP_a
SVC_a e4@--> HR_b
HR_b -."parentRef: override".-> SVC_a
HR_b e5@--> SVC_b
SVC_b e6@--> DEP_b
subgraph Notes
direction TB
N1["Note 1: A Route that uses a Service as parentRef **replaces** the implicit routing of that Service (GEP-1294 'Route presence'). Therefore, the selector on the Service side does not affect the final destination determination."]:::note
end
e1@{ animate: true }
e2@{ animate: true }
e4@{ animate: true }
e5@{ animate: true }
e6@{ animate: true }
Use Cases
- Without modifying HTTPRoute managed by a Controller, you can switch the destination Service or add necessary L7 controls (match/header manipulation/retry/split, etc.) using another HTTPRoute with
Serviceas parent - "I want to control all inbound traffic to that Service" → producer route
- "I want to redirect only outbound traffic from my Namespace" → consumer route
Additional Explanation
In GEP-1294, the meaning changes based on the relationship between the Namespace of the Service pointed to by parentRef and the Namespace of the Route. There is no explicit field in the API; this is purely a distinction in behavior.
This enables the following scenarios:
- When you want to control the entire ingress point, define a producer route on the target Service side
- When you want to control only outbound traffic from your Namespace, create a consumer route in a different Namespace
In both cases, this can be achieved by adding routes without touching existing HTTPRoute.
producer route (control ingress side)
Application conditions:
When parentRef points to a Service in the same Namespace
Behavior:
- SHOULD be applied to all inbound requests to that Service (including calls from other Namespaces)
- Route rules take precedence over the Service's default routing
- Note: Some implementations apply this at the client-side proxy, so it may not affect clients outside the mesh
The diagram shown earlier illustrates this producer route.
consumer route (control egress side)
Application conditions:
When parentRef points to a Service in a different Namespace
Behavior:
- Applied only to egress traffic from within the Namespace where the Route exists (limited scope)
- Provides boundaries to prevent unrelated third-party Namespaces from arbitrarily changing traffic between different Namespaces
① A certain namespace has HTTPRoute a applied (consumer route / normal path)
flowchart LR
classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;
subgraph app-ns
SVC_a[(Service a)]
DEP_a[Deployment a]
end
subgraph other-ns["other-ns (e.g., some ns)"]
CALL_other["Pod/Sidecar (caller)"]
HR_a["HTTPRoute a (consumer)"]
end
CALL_other e1@--> HR_a
HR_a -."parentRef: app-ns / Service a (different NS ↔ consumer)".-> SVC_a
HR_a e2@--> SVC_a
SVC_a e3@--> DEP_a
e1@{ animate: true }
e2@{ animate: true }
e3@{ animate: true }
② A specific namespace has HTTPRoute b applied (consumer route / destination redirect)
flowchart LR
classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;
subgraph app-ns
SVC_a[(Service a)]
end
subgraph ext-ns
SVC_b[(Service b)]
DEP_b[Deployment b]
end
subgraph dev-ns["dev-ns (specific ns)"]
CALL_dev["Pod/Sidecar (caller)"]
HR_b["HTTPRoute b (consumer)"]
end
CALL_dev e1@--> HR_b
HR_b -."parentRef: app-ns / Service a (different NS ↔ consumer)".-> SVC_a
HR_b e2@--> SVC_b
SVC_b e3@--> DEP_b
e1@{ animate: true }
e2@{ animate: true }
e3@{ animate: true }
Example
- Set up HTTPRoute normally with Gateway API
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gw
namespace: app-ns
spec:
gatewayClassName: istio
listeners:
- name: http
protocol: HTTP
port: 80
hostname: example.local
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: route-a
namespace: app-ns
spec:
hostnames:
- example.local
parentRefs:
- name: gw
namespace: app-ns
rules:
- backendRefs:
- kind: Service
name: svc-a
port: 80
weight: 100
---
apiVersion: v1
kind: Service
metadata:
name: svc-a
namespace: app-ns
spec:
selector:
app: a
ports:
- name: http
port: 80
targetPort: 5678
- Without modifying the above HTTPRoute or Service, override the entire ingress point with a producer route (place
route-binapp-ns)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: route-b
namespace: app-ns # Same NS, so this is a producer route
spec:
parentRefs:
- group: ""
kind: Service
name: svc-a
namespace: app-ns
port: 80
rules:
- backendRefs:
- kind: Service
name: svc-b
namespace: ext-ns # Switch to destination in different NS
port: 80
weight: 100
---
apiVersion: v1
kind: Service
metadata:
name: svc-b
namespace: ext-ns
spec:
selector:
app: b
ports:
- name: http
port: 80
targetPort: 5678
Since the above is a producer route (parent reference in the same NS), the rules of
route-bare applied to all inbound requests tosvc-a, and the destination is redirected tosvc-b(depending on the mesh implementation, the application point may be on the client side).
Note on ReferenceGrant
GEP-1294 explicitly states that in the Mesh (East/West) context, Routes with Service as parentRef can reference backendRefs in different Namespaces and are allowed without ReferenceGrant:
Routes of either type can send traffic to backendRefs in any namespace. Unlike Gateway bound routes, this is allowed without a ReferenceGrant. In Gateway-bound routes (North-South), routes are opt-in; by default, no Services are exposed (often to the public internet), and a service producer must explicitly opt-in by creating a route themselves, or allowing another namespace to via ReferenceGrant. For mesh, routes augment existing Services, rather than exposing them to a broader scope.
Additionally, it notes that access control should be handled by mechanisms such as NetworkPolicy:
As a result, a ReferenceGrant is not required in most mesh implementations. Access control, if desired, is handled by other mechanism such as NetworkPolicy
Therefore, the configuration in this article where a producer route in app-ns routes to svc-b in ext-ns does not require ReferenceGrant.
Summary
I introduced a method to change the reference destination of HTTPRoute generated by other controllers using GEP-1294, achieving the same result as if you had written additional configuration in the HTTPRoute, when you cannot customize that HTTPRoute directly.
I hope this serves as a reference for someone.
Reference Links
- Route presence: https://gateway-api.sigs.k8s.io/geps/gep-1294/#route-presence
- Namespace boundaries (producer / consumer boundaries): https://gateway-api.sigs.k8s.io/geps/gep-1294/#namespace-boundaries



Top comments (0)