Kubernetes Gateway API: HTTPRoute Examples
Gateway API examples: GatewayClass, Gateway listeners, HTTPRoute, GRPCRoute, TLSRoute, canary weights, HTTPS redirect, ReferenceGrant, Ingress migration.
💡 Quick Answer: Gateway API replaces Ingress with more features. Install CRDs (
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml), deploy a GatewayClass + Gateway, then create HTTPRoute resources to route traffic. Routes attach to Gateways withparentRefsand define path/header-based routing rules.Key concept: GatewayClass (infrastructure) → Gateway (listener config) → HTTPRoute (routing rules)—separating infrastructure and app team concerns.
Gotcha: You need a Gateway implementation (NGINX Gateway Fabric, Envoy Gateway, Istio)—the CRDs alone don’t route traffic.
The Problem
Kubernetes Ingress has limitations: vendor-specific annotations, no support for TCP/UDP routing, and limited traffic splitting capabilities. You need a more expressive, portable, and role-oriented API for traffic management.
The Solution
Use the Gateway API, the next-generation Kubernetes networking standard that provides expressive, extensible, and role-oriented routing for HTTP, HTTPS, TCP, and gRPC traffic.
Gateway API vs Ingress
| Feature | Ingress | Gateway API |
|---|---|---|
| Traffic splitting | Annotations only | Native weight on backendRefs |
| Header / query matching | Annotations only | Native |
| URL rewrite, redirects, header mods | Annotations only | Native filters |
| gRPC routing | No | GRPCRoute (GA, standard channel) |
| TLS passthrough / TCP / UDP | No | TLSRoute, TCPRoute, UDPRoute (experimental) |
| Cross-namespace, multi-tenant | No | allowedRoutes + ReferenceGrant |
| Role separation | One resource | GatewayClass / Gateway / Route |
flowchart TB
subgraph model["🎯 ROLE-BASED RESOURCE MODEL"]
GatewayClass["🏗️ GatewayClass<br/>(nginx, envoy)"]
Gateway["🚪 Gateway<br/>(listeners, addresses)"]
Routes["📍 HTTPRoute<br/>📍 TCPRoute<br/>📍 TLSRoute<br/>📍 GRPCRoute"]
GatewayClass --> Gateway --> Routes
GatewayClass -.- admin["👤 Infrastructure Provider<br/>(cluster-admin)"]
Gateway -.- platform["👥 Cluster Operator<br/>(platform team)"]
Routes -.- dev["💻 Application Developer<br/>(dev team)"]
endStep 1: Install Gateway API CRDs
# Standard channel: GatewayClass, Gateway, HTTPRoute, GRPCRoute, ReferenceGrant
# (check https://github.com/kubernetes-sigs/gateway-api/releases for the latest tag
# and the version your controller supports)
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
# Experimental channel adds TLSRoute, TCPRoute, UDPRoute
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/experimental-install.yaml
# Verify installation
kubectl get crds | grep gatewayStep 2: Install a Gateway Controller
Option A: NGINX Gateway Fabric
# Install NGINX Gateway Fabric (Helm)
helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
-n nginx-gateway --create-namespace
# Verify deployment
kubectl get pods -n nginx-gatewayOption B: Envoy Gateway
# Install Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \
-n envoy-gateway-system --create-namespace
# Verify
kubectl get pods -n envoy-gateway-systemOption C: Istio Gateway
# Gateway API support is on by default in current Istio releases
istioctl install --set profile=minimalOption D: Cilium
cilium install --set kubeProxyReplacement=true --set gatewayAPI.enabled=trueOn OpenShift 4.19+, the Ingress Operator ships Gateway API support backed by OpenShift Service Mesh; create a GatewayClass with controllerName: openshift.io/gateway-controller/v1.
Step 3: Create GatewayClass and Gateway
GatewayClass (Infrastructure Provider)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: gateway.nginx.org/nginx-gateway-controllerGateway (Platform Team)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: default
spec:
gatewayClassName: nginx
listeners:
# HTTP listener
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
# HTTPS listener
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
kind: Secret
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true"
# Specific hostname listener
- name: api
protocol: HTTPS
port: 443
hostname: "api.example.com"
tls:
mode: Terminate
certificateRefs:
- name: api-tls
allowedRoutes:
namespaces:
from: SameStep 4: Configure HTTPRoute
Basic HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-app-route
namespace: production
spec:
parentRefs:
- name: main-gateway
namespace: default
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-app
port: 80Path-Based Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: microservices-route
namespace: production
spec:
parentRefs:
- name: main-gateway
hostnames:
- "api.example.com"
rules:
# Route /users to users service
- matches:
- path:
type: PathPrefix
value: /users
backendRefs:
- name: users-service
port: 8080
# Route /orders to orders service
- matches:
- path:
type: PathPrefix
value: /orders
backendRefs:
- name: orders-service
port: 8080
# Route /products to products service
- matches:
- path:
type: PathPrefix
value: /products
backendRefs:
- name: products-service
port: 8080
# Default backend
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: frontend
port: 80Header-Based Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-routing
namespace: production
spec:
parentRefs:
- name: main-gateway
hostnames:
- "api.example.com"
rules:
# Route mobile traffic to mobile backend
- matches:
- headers:
- name: X-Client-Type
value: mobile
backendRefs:
- name: mobile-backend
port: 8080
# Route based on API version header
- matches:
- headers:
- name: X-API-Version
value: v2
backendRefs:
- name: api-v2
port: 8080
# Default to v1
- backendRefs:
- name: api-v1
port: 8080Query Parameter Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: query-routing
spec:
parentRefs:
- name: main-gateway
hostnames:
- "api.example.com"
rules:
# Route debug requests
- matches:
- queryParams:
- name: debug
value: "true"
backendRefs:
- name: debug-service
port: 8080
# Default route
- backendRefs:
- name: production-service
port: 8080Traffic Splitting (Canary/Blue-Green)
Weighted Traffic Split
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-deployment
namespace: production
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.example.com"
rules:
- backendRefs:
# 90% to stable
- name: app-stable
port: 8080
weight: 90
# 10% to canary
- name: app-canary
port: 8080
weight: 10Header-Based Canary
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-canary
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.example.com"
rules:
# Canary for specific users
- matches:
- headers:
- name: X-Canary
value: "true"
backendRefs:
- name: app-canary
port: 8080
# Production for everyone else
- backendRefs:
- name: app-stable
port: 8080Request/Response Modification
URL Rewriting
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: url-rewrite
spec:
parentRefs:
- name: main-gateway
hostnames:
- "api.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api/v1
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /v1
backendRefs:
- name: backend
port: 8080Header Modification
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-modification
spec:
parentRefs:
- name: main-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
# Add request headers
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Request-ID
value: "generated-id"
- name: X-Forwarded-Proto
value: https
set:
- name: Host
value: backend.internal
remove:
- X-Debug-Header
# Add response headers
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: X-Response-Time
value: "100ms"
set:
- name: Cache-Control
value: "max-age=3600"
backendRefs:
- name: backend
port: 8080Redirects
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirects
spec:
parentRefs:
- name: main-gateway
hostnames:
- "old.example.com"
rules:
# Redirect to new domain
- filters:
- type: RequestRedirect
requestRedirect:
hostname: new.example.com
statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-redirect
spec:
parentRefs:
- name: main-gateway
sectionName: http # Reference HTTP listener
rules:
# Redirect HTTP to HTTPS
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301TLS Configuration
TLS Termination
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: nginx
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "secure.example.com"
tls:
mode: Terminate
certificateRefs:
- name: secure-tls-cert
kind: Secret
allowedRoutes:
namespaces:
from: SameTLS Passthrough
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: passthrough-gateway
spec:
gatewayClassName: nginx
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
hostname: "backend.example.com"
tls:
mode: Passthrough
allowedRoutes:
kinds:
- kind: TLSRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: backend-tls-route
spec:
parentRefs:
- name: passthrough-gateway
hostnames:
- "backend.example.com"
rules:
- backendRefs:
- name: backend-service
port: 443GRPCRoute
apiVersion: gateway.networking.k8s.io/v1 # GA since Gateway API v1.1
kind: GRPCRoute
metadata:
name: grpc-route
spec:
parentRefs:
- name: main-gateway
hostnames:
- "grpc.example.com"
rules:
# Route by service name
- matches:
- method:
service: myapp.UserService
backendRefs:
- name: user-grpc-service
port: 50051
# Route by method
- matches:
- method:
service: myapp.OrderService
method: CreateOrder
backendRefs:
- name: order-grpc-service
port: 50051TCPRoute
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: database-route
spec:
parentRefs:
- name: tcp-gateway
sectionName: postgres
rules:
- backendRefs:
- name: postgres-service
port: 5432
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tcp-gateway
spec:
gatewayClassName: nginx
listeners:
- name: postgres
protocol: TCP
port: 5432
allowedRoutes:
kinds:
- kind: TCPRouteCross-Namespace Routing
Allow Routes from Specific Namespaces
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: gateway-system
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"Reference Gateway from Another Namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: team-a # Must have label: shared-gateway-access: "true"
spec:
parentRefs:
- name: shared-gateway
namespace: gateway-system
hostnames:
- "team-a.example.com"
rules:
- backendRefs:
- name: team-a-app
port: 8080ReferenceGrant (Cross-Namespace Backend)
# Allow HTTPRoute in 'frontend' namespace to reference
# Service in 'backend' namespace
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-frontend-to-backend
namespace: backend
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: frontend
to:
- group: ""
kind: ServiceVerification Commands
# Check Gateway status
kubectl get gateway main-gateway -o yaml
# Check HTTPRoute status
kubectl get httproute web-app-route -o yaml
# View attached routes
kubectl describe gateway main-gateway
# Check GatewayClass
kubectl get gatewayclass
# Debug routing
kubectl get httproutes -A -o wide
# Is the route Accepted and are refs Resolved?
kubectl get httproute web-app-route -o jsonpath='{.status.parents[*].conditions}' | jq
# Test routing
curl -H "Host: app.example.com" http://<gateway-ip>/Migration from Ingress
Before (Ingress)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080After (Gateway API)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 8080Common Issues
HTTPRoute not working — no traffic. The route isn’t attached. Check status.parents[].conditions: Accepted=False usually means the route’s namespace isn’t allowed by the listener’s allowedRoutes, or sectionName/hostname doesn’t match any listener.
Gateway stuck with no address / not Programmed. No controller owns the GatewayClass. Verify kubectl get gatewayclass shows ACCEPTED=True and controllerName matches the installed controller.
ResolvedRefs=False / RefNotPermitted. A backendRef or certificateRef points to another namespace without a ReferenceGrant in the target namespace.
Frequently Asked Questions
What is the difference between Gateway API and Ingress?
Ingress is a single resource with controller-specific annotations for anything beyond host/path routing. Gateway API splits infrastructure (GatewayClass, Gateway) from routing (HTTPRoute, GRPCRoute) and makes traffic splitting, header matching, rewrites and cross-namespace routing part of the standard API.
How do I redirect HTTP to HTTPS with Gateway API?
Attach an HTTPRoute to the HTTP listener with parentRefs[].sectionName: http and a RequestRedirect filter with scheme: https and statusCode: 301 (see Redirects above). Attach your real routes to the HTTPS listener only.
Is GRPCRoute stable?
Yes. GRPCRoute moved to gateway.networking.k8s.io/v1 and the standard channel in Gateway API v1.1. TLSRoute, TCPRoute and UDPRoute remain experimental (v1alpha2).
How many rules can a TLSRoute have?
The TLSRoute spec allows 1 to 16 rules per route. In practice use one rule per route and set hostnames for SNI matching.
Which Gateway API implementation should I use?
Envoy Gateway, Istio, Cilium, NGINX Gateway Fabric, Traefik and Kong are all conformant for HTTPRoute. Pick the one that matches your existing stack (service mesh, CNI, or OpenShift’s built-in support) and check its conformance report for the features you need.
Summary
The Gateway API provides a more expressive, role-oriented approach to Kubernetes traffic management. It separates concerns between infrastructure providers, platform teams, and developers while offering powerful routing capabilities beyond traditional Ingress.
📘 Go Further with Kubernetes Recipes
Love this recipe? There’s so much more! This is just one of 100+ hands-on recipes in our comprehensive Kubernetes Recipes book.
Inside the book, you’ll master:
- ✅ Production-ready deployment strategies
- ✅ Advanced networking and security patterns
- ✅ Observability, monitoring, and troubleshooting
- ✅ Real-world best practices from industry experts
“The practical, recipe-based approach made complex Kubernetes concepts finally click for me.”
👉 Get Your Copy Now — Start building production-grade Kubernetes skills today!

Recommended
Kubernetes Recipes — The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book →Learn by Doing
CopyPasteLearn — Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses →🎓 Deepen Your Skills — Hands-on Courses
Courses by CopyPasteLearn.com — Learn IT by Doing
