Orchestration

Gateway API HTTPRoute: Advanced Traffic Routing

September 1, 2026 Kubezilla Team 9 min read

Introduction

In the dynamic landscape of modern cloud-native applications, efficient and flexible traffic management is paramount. Kubernetes, the de facto standard for container orchestration, offers various mechanisms for exposing services, but none provide the granular control and extensibility needed for complex routing scenarios quite like the Kubernetes Gateway API. Designed as a successor and more powerful alternative to the venerable Ingress API, Gateway API introduces a robust, role-oriented, and extensible approach to network traffic management, particularly for HTTP/HTTPS services.

At the heart of the Gateway API lies the `HTTPRoute` resource, which is your primary tool for defining how HTTP and HTTPS traffic is routed from a `Gateway` to your Kubernetes services. Beyond basic path-based or host-based routing, `HTTPRoute` unlocks a treasure trove of advanced patterns: from sophisticated header matching and query parameter manipulation to traffic splitting for A/B testing, blue/green deployments, and canary releases. Mastering `HTTPRoute` is crucial for anyone looking to build resilient, scalable, and highly observable applications on Kubernetes. This guide will deep-dive into these advanced patterns, providing practical, copy-paste-ready examples to elevate your traffic routing game. For a broader understanding of migrating from Ingress, check out our Gateway API Migration Guide.

TL;DR

The Kubernetes Gateway API’s HTTPRoute resource offers powerful, advanced traffic routing. It allows for host-based, path-based, header-based, and query parameter-based matching, traffic splitting, and request/response manipulation. This guide demonstrates how to deploy a Gateway and `HTTPRoute`s to implement these patterns for sophisticated application delivery.

# Install Gateway API CRDs (if not already present)
kubectl apply -k "https://github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.0.0"

# Install a Gateway Controller (e.g., NGINX Gateway Fabric)
helm upgrade --install ngf oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --namespace nginx-gateway --create-namespace

# Deploy a Gateway
kubectl apply -f <your-gateway.yaml>

# Deploy services and HTTPRoutes
kubectl apply -f <your-application-services.yaml>
kubectl apply -f <your-httproute-advanced.yaml>

# Get Gateway address
kubectl get gateway <your-gateway-name> -n <your-namespace> -o jsonpath='{.status.addresses[0].value}'

# Test routing patterns (replace IP with Gateway address)
curl -H "X-Version: v2" http://<GATEWAY_IP>/api/v1/users
curl -H "User-Agent: mobile" http://<GATEWAY_IP>/app/v1/data
curl http://<GATEWAY_IP>/weighted-split

Prerequisites

To follow this tutorial, you’ll need the following:

  • Kubernetes Cluster: A running Kubernetes cluster (v1.24+ recommended for Gateway API v1.0.0 support). Minikube, Kind, or any cloud provider cluster will work.
  • kubectl: The Kubernetes command-line tool, configured to connect to your cluster. Refer to the official Kubernetes documentation for installation instructions.
  • helm: The Kubernetes package manager, used for deploying the Gateway API controller. Install it from the Helm website.
  • Basic Kubernetes Knowledge: Familiarity with Deployments, Services, and Namespaces.
  • Basic Gateway API Concepts: An understanding of GatewayClass and Gateway resources. If you’re new, our Gateway API Migration Guide is a great starting point.
  • curl or similar HTTP client: For testing the routing patterns.

Step-by-Step Guide: Advanced HTTPRoute Patterns

This guide will walk you through deploying a Gateway API controller, setting up a Gateway, and then creating various `HTTPRoute` configurations to demonstrate advanced traffic routing. We’ll use the NGINX Gateway Fabric as our Gateway controller for this example, but the `HTTPRoute` configurations are largely controller-agnostic.

1. Install Gateway API CRDs and a Gateway Controller

First, we need to ensure the Gateway API Custom Resource Definitions (CRDs) are installed in your cluster. These CRDs define the GatewayClass, Gateway, HTTPRoute, and other resources. Then, we’ll deploy a Gateway controller that implements these CRDs. We’ll use NGINX Gateway Fabric as an example controller, which is a popular choice for its performance and feature set. Other controllers like Istio (especially with Istio Ambient Mesh) or Kong Gateway also support Gateway API.

# Install Gateway API CRDs (v1.0.0 is stable)
kubectl apply -k "https://github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.0.0"

# Add the NGINX Gateway Fabric Helm repository
helm repo add nginx-gateway-fabric https://nginxinc.github.io/nginx-gateway-fabric/

# Update Helm repositories
helm repo update

# Install NGINX Gateway Fabric controller
# We'll install it in its own namespace for isolation.
helm upgrade --install ngf nginx-gateway-fabric/nginx-gateway-fabric \
  --namespace nginx-gateway --create-namespace \
  --set gatewayClass.controllerName=nginx.org/gateway-controller

Verify

Check if the Gateway API CRDs are installed and the NGINX Gateway Fabric pods are running.

kubectl get crd | grep gatewayapi.k8s.io
kubectl get pods -n nginx-gateway

Expected Output:

# Example CRD output (may vary slightly based on version)
gatewayclasses.gateway.networking.k8s.io           2023-10-26T14:30:00Z
gateways.gateway.networking.k8s.io                 2023-10-26T14:30:00Z
httproutes.gateway.networking.k8s.io               2023-10-26T14:30:00Z
...

# Example Pods output
NAME                                           READY   STATUS    RESTARTS   AGE
nginx-gateway-fabric-controller-5c9f8d68-2l8sk   1/1     Running   0          2m

2. Deploy a Gateway and Sample Applications

Next, we’ll define a GatewayClass and a Gateway resource. The GatewayClass references our NGINX controller, and the Gateway resource defines the entry point for external traffic, specifying listeners (e.g., HTTP on port 80). We’ll also deploy a few simple NGINX applications that we can use to demonstrate different routing patterns.

# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: nginx.org/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: default # Can be in any namespace, but for simplicity, default
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All # Allow HTTPRoutes from any namespace to attach
---
# application-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-v1
  labels:
    app: app
    version: v1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
      version: v1
  template:
    metadata:
      labels:
        app: app
        version: v1
    spec:
      containers:
      - name: app
        image: nginxdemos/hello:plain-text
        ports:
        - containerPort: 80
        env:
        - name: MESSAGE
          value: "Hello from App V1!"
---
apiVersion: v1
kind: Service
metadata:
  name: app-v1-svc
spec:
  selector:
    app: app
    version: v1
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
---
# application-v2.yaml (for traffic splitting, header matching)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-v2
  labels:
    app: app
    version: v2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
      version: v2
  template:
    metadata:
      labels:
        app: app
        version: v2
    spec:
      containers:
      - name: app
        image: nginxdemos/hello:plain-text
        ports:
        - containerPort: 80
        env:
        - name: MESSAGE
          value: "Hello from App V2!"
---
apiVersion: v1
kind: Service
metadata:
  name: app-v2-svc
spec:
  selector:
    app: app
    version: v2
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
kubectl apply -f gateway.yaml
kubectl apply -f application-v1.yaml
kubectl apply -f application-v2.yaml

Verify

Ensure the Gateway is provisioned and has an IP address, and that the application pods are running.

kubectl get gateway my-gateway -o wide
kubectl get pods -l app=app

Expected Output:

# Example Gateway output (IP will vary)
NAME         CLASS   ADDRESS        PROGRAMMED   AGE
my-gateway   nginx   192.168.1.10   True         2m

# Example Pods output
NAME                       READY   STATUS    RESTARTS   AGE
app-v1-56789abcd-efghj     1/1     Running   0          1m
app-v1-56789abcd-klmno     1/1     Running   0          1m
app-v2-pqrstuvw-xyz12      1/1     Running   0          1m
app-v2-pqrstuvw-34567      1/1     Running   0          1m

Note the ADDRESS of your Gateway. This is the IP you’ll use for testing.

3. Basic Host and Path Matching

Before diving into advanced patterns, let’s establish a baseline with basic host and path matching. This `HTTPRoute` will route traffic for example.com and api.example.com to different services based on the host and path prefix.

# httproute-basic.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: basic-routing
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "example.com"
  - "api.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
    backendRefs:
    - name: app-v2-svc
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app-v1-svc
      port: 80
kubectl apply -f httproute-basic.yaml

Verify

You’ll need to simulate host headers for these tests, as we likely don’t have DNS configured for example.com pointing to our Gateway IP. Replace <GATEWAY_IP> with your Gateway’s external IP address.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

# Test host example.com, path /
curl -H "Host: example.com" http://$GATEWAY_IP/
curl -H "Host: example.com" http://$GATEWAY_IP/some/other/path

# Test host api.example.com, path /api/v1
curl -H "Host: api.example.com" http://$GATEWAY_IP/api/v1/users
curl -H "Host: api.example.com" http://$GATEWAY_IP/api/v1/products

Expected Output:

# For example.com requests
Hello from App V1!
# For api.example.com/api/v1 requests
Hello from App V2!

4. Header-Based Routing

Header-based routing allows you to direct traffic to different services based on specific HTTP headers present in the request. This is incredibly useful for A/B testing, feature flagging, or routing internal tools. For instance, you might route requests with a specific X-Version header to a new version of your application.

# httproute-header-matching.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-routing
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
      headers:
      - name: X-Version
        value: v2
        type: Exact # Match header exactly
    backendRefs:
    - name: app-v2-svc
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
    backendRefs:
    - name: app-v1-svc
      port: 80
kubectl apply -f httproute-header-matching.yaml

Verify

We’ll test sending requests with and without the X-Version: v2 header. Remember to use the `app.example.com` host header.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

# Request without X-Version header (should go to v1)
curl -H "Host: app.example.com" http://$GATEWAY_IP/api/v1/users

# Request with X-Version: v2 header (should go to v2)
curl -H "Host: app.example.com" -H "X-Version: v2" http://$GATEWAY_IP/api/v1/users

Expected Output:

# For request without header
Hello from App V1!
# For request with X-Version: v2 header
Hello from App V2!

5. Query Parameter-Based Routing

Similar to header matching, `HTTPRoute` can also match based on query parameters. This is useful for routing traffic based on specific parameters in the URL, such as feature flags (e.g., ?feature=new-ui) or debugging requests.

# httproute-query-param-matching.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: query-param-routing
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "debug.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /app
      queryParams:
      - name: debug
        value: true
        type: Exact # Match query param exactly
    backendRefs:
    - name: app-v2-svc # Route debug requests to v2
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /app
    backendRefs:
    - name: app-v1-svc
      port: 80
kubectl apply -f httproute-query-param-matching.yaml

Verify

Test with and without the ?debug=true query parameter. Use the `debug.example.com` host header.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

# Request without debug query param (should go to v1)
curl -H "Host: debug.example.com" http://$GATEWAY_IP/app/data

# Request with debug=true query param (should go to v2)
curl -H "Host: debug.example.com" http://$GATEWAY_IP/app/data?debug=true

Expected Output:

# For request without query param
Hello from App V1!
# For request with debug=true query param
Hello from App V2!

6. Traffic Splitting (Weighted Round Robin)

Traffic splitting is a cornerstone of modern deployment strategies like canary releases and A/B testing. `HTTPRoute` allows you to distribute traffic across multiple backend services based on a weight, enabling gradual rollouts or comparing different versions. This is a powerful feature for reducing risk and validating new features in production. For advanced cluster scaling, consider tools like Karpenter for Cost Optimization.

# httproute-traffic-splitting.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: weighted-traffic
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "split.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app-v1-svc
      port: 80
      weight: 80 # 80% of traffic to v1
    - name: app-v2-svc
      port: 80
      weight: 20 # 20% of traffic to v2
kubectl apply -f httproute-traffic-splitting.yaml

Verify

Repeatedly send requests to the Gateway. You should observe responses from both `app-v1` and `app-v2`, with `app-v1` appearing more frequently. Use the `split.example.com` host header.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

echo "Testing weighted traffic split (80% to v1, 20% to v2):"
for i in $(seq 1 10); do
  curl -s -H "Host: split.example.com" http://$GATEWAY_IP/
done

Expected Output:

# You should see a mix, predominantly "Hello from App V1!" with some "Hello from App V2!"
Hello from App V1!
Hello from App V1!
Hello from App V2!
Hello from App V1!
Hello from App V1!
Hello from App V1!
Hello from App V1!
Hello from App V2!
Hello from App V1!
Hello from App V1!

7. Request and Response Header Manipulation

The Gateway API allows you to modify HTTP request and response headers. This is incredibly powerful for injecting context, setting security policies, or transforming responses before they reach the client. For example, you might add a unique request ID, set CORS headers, or remove sensitive information.

# httproute-header-manipulation.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-manipulation
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "transform.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: RequestHeaderModifier
      requestHeaderModifier:
        add:
        - name: X-Request-ID
          value: "generated-by-gateway"
        set:
        - name: X-App-Source
          value: "gateway-api"
        remove:
        - User-Agent # Remove User-Agent header from request to backend
    - type: ResponseHeaderModifier
      responseHeaderModifier:
        add:
        - name: X-Gateway-Processed
          value: "true"
        set:
        - name: Cache-Control
          value: "no-cache, no-store, must-revalidate"
        remove:
        - X-Powered-By # Remove potentially revealing header from response
    backendRefs:
    - name: app-v1-svc
      port: 80
kubectl apply -f httproute-header-manipulation.yaml

Verify

We’ll use curl -v to inspect the request and response headers. The NGINX backend application doesn’t directly show received request headers in its body, but the Gateway controller will process them. We will observe the *response* headers directly.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

# Make a request and observe response headers
curl -v -H "Host: transform.example.com" http://$GATEWAY_IP/

Expected Output (snippet from curl -v):

...
< HTTP/1.1 200 OK
< X-Gateway-Processed: true # Added by Gateway
< Cache-Control: no-cache, no-store, must-revalidate # Set by Gateway
< Date: ...
< Content-Type: text/plain
< Content-Length: 19
< Connection: keep-alive
<
Hello from App V1!

You should see X-Gateway-Processed: true and Cache-Control: no-cache, no-store, must-revalidate in the response headers. The X-Powered-By header (if present from the backend) should be removed. The request headers modified by the Gateway would be visible if you had a backend service that logs or reflects all incoming headers.

8. URL Rewriting

URL rewriting allows you to change the path of a request before it reaches the backend service. This is useful for exposing a service at a different path than its internal path, or for simplifying external URLs. For example, you might expose /public externally, but internally route it to /internal-api.

# httproute-url-rewrite.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: url-rewrite
spec:
  parentRefs:
  - name: my-gateway
    namespace: default
  hostnames:
  - "rewrite.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /old-path
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /new-path # Rewrites /old-path to /new-path
    backendRefs:
    - name: app-v1-svc
      port: 80
  # Another rule for a different rewrite type (e.g., replace hostname)
  - matches:
    - path:
        type: PathPrefix
        value: /host-rewrite
    filters:
    - type: URLRewrite
      urlRewrite:
        hostname: "app-v2-svc.default.svc.cluster.local" # Rewrites hostname to internal service FQDN
        path:
          type: ReplaceFullPath
          replaceFullPath: / # Routes to root of new hostname
    backendRefs:
    - name: app-v2-svc
      port: 80
kubectl apply -f httproute-url-rewrite.yaml

Verify

The NGINX demo application doesn’t reflect the requested path in its output, so we need to infer the rewrite by checking which service responds. If /old-path routes to app-v1-svc and /host-rewrite routes to app-v2-svc, we know the rewrite is working as the target backend receives the modified request.

GATEWAY_IP=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')

# Test path rewrite: /old-path should go to app-v1-svc
curl -H "Host: rewrite.example.com" http://$GATEWAY_IP/old-path/some-subpath

# Test host and path rewrite: /host-rewrite should go to app-v2-svc
curl -H "Host: rewrite.example.com" http://$GATEWAY_IP/host-rewrite

Expected Output:

# For /old-path
Hello from App V1!
# For /host-rewrite
Hello from App V2!

Production Considerations

Implementing advanced `HTTPRoute` patterns in production requires careful planning and attention to several key areas:

  1. Observability: Robust monitoring and logging are crucial. Ensure your Gateway controller and backend services emit metrics (e.g., Prometheus) and logs that can be easily collected and analyzed. This is essential for understanding traffic flow, identifying issues, and validating routing rules. Tools leveraging eBPF for Observability with Hubble can provide deep network insights.
  2. Security:
    • TLS Configuration: Always use HTTPS for production traffic. Configure TLS termination on your Gateway using `TLSRoute` or directly within `Gateway` listeners.
    • Authorization: Integrate with external authorization systems (e.g., OPA, OAuth2 proxies) if `HTTPRoute`’s native capabilities are insufficient.
    • Network Policies: Use Kubernetes Network Policies to restrict traffic flow between your Gateway controller and backend services, as well as between services themselves. Our Network Policies Security Guide provides comprehensive hardening strategies.
    • Supply Chain Security: Ensure the Gateway controller images and configurations are secured. Projects like Sigstore and Kyverno can help enforce image signing and policy-as-code for your deployments.
  3. Performance & Scalability:
    • Gateway Controller Sizing: Properly size your Gateway controller instances (CPU, memory) to handle anticipated traffic loads.
    • Horizontal Scaling: Ensure your Gateway controller can scale horizontally to meet demand.
    • Backend Scaling: Implement Horizontal Pod Autoscalers (HPAs) for your backend services to ensure they can handle the routed traffic.
  4. Testing Strategy:
    • Unit/Integration Tests: Test your `HTTPRoute` configurations in development environments.
    • Automated E2E Tests: Implement end-to-end tests that hit your Gateway and verify correct routing and responses.
    • Canary/A/B Testing: Leverage `HTTPRoute`’s traffic splitting capabilities for safe, gradual rollouts and experimentation.
  5. Backup and Disaster Recovery: Regularly back up your Kubernetes cluster state, including Gateway API resources. Plan for how to restore your traffic routing in case of a disaster.
  6. GitOps: Manage your Gateway API resources (GatewayClass, Gateway, HTTPRoute, etc.) using GitOps principles. Store all configurations in a Git repository and use tools like Argo CD or Flux CD for automated deployment and synchronization.

Troubleshooting

Here are some common issues you might encounter when working with Gateway API `HTTPRoute` and their solutions.

1. Gateway Not Provisioned / Pending Address

Issue: Your `Gateway` resource remains in `Pending` status or doesn’t get an `ADDRESS` assigned.

Solution:

  1. Check GatewayClass: Ensure the `GatewayClass` specified in your `Gateway` exists and its `controllerName` matches the controller you installed (e.g., `nginx.org/gateway-controller`).
  2. Check Controller Pods: Verify that your Gateway controller pods are running without errors in their designated namespace (`nginx-gateway` in our example).
    kubectl get gatewayclass
    kubectl get pods -n nginx-gateway
    kubectl logs -f -n nginx-gateway <nginx-gateway-fabric-pod-name>
    
  3. Service Type: If running on a cloud provider, ensure your controller’s service (often a `LoadBalancer` type) can provision an external IP. Check cloud provider logs if the `LoadBalancer` creation fails. If local (Minikube, Kind), ensure your environment supports `LoadBalancer` services (e.g., Minikube’s tunnel or MetalLB for Kind).

2. HTTPRoute Not Attaching to Gateway

Issue: Your `HTTPRoute` doesn’t seem to be routing traffic, and its `status.parents` field might show `Programmed: False` or `Accepted: False`.

Solution:

  1. `parentRefs` Mismatch: Verify that the `parentRefs` in your `HTTPRoute` correctly specify the `name` and `namespace` of your `Gateway`.
  2. `allowedRoutes` Configuration: Check the `allowedRoutes` section of your `Gateway` listener. It dictates which namespaces can attach `HTTPRoute`s. If `from: Same` is used, the `HTTPRoute` must be in the same namespace as the `Gateway`. `from: All` allows any namespace.
    kubectl describe httproute <your-httproute-name>
    kubectl describe gateway <my-gateway>
    
  3. Hostname Conflicts: Ensure there are no conflicting `HTTPRoute`s claiming the same hostname/path combination with higher precedence.

3. Traffic Not Reaching Backend Service

Issue:

Leave a comment