HTTP/2

You might have services in your Kubernetes cluster that use HTTP/2 for communication. Typically these are gRPC services, but it could apply to any service that uses HTTP/2 in its transport layer. To enable HTTP/2 communication, you simply set the app protocol on the service to HTTP/2. This setting instructs kgateway to use HTTP/2 for communication with the destination.

The docs in this section use the Envoy-based kgateway proxy. The docs do not work with the agentgateway proxy.

Before you begin

  1. Follow the Get started guide to install kgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  
    kubectl port-forward deployment/http -n kgateway-system 8080:8080

Enable access logging

Enable access logging on the Gateway. You can use the access logs later to verify that the request used the HTTP/2 protocol.

kubectl apply -f- <<EOF
apiVersion: gateway.kgateway.dev/v1alpha1
kind: HTTPListenerPolicy
metadata:
  name: access-logs
  namespace: kgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: http
  accessLog:
  - fileSink:
      path: /dev/stdout
      jsonFormat:
          start_time: "%START_TIME%"
          method: "%REQ(X-ENVOY-ORIGINAL-METHOD?:METHOD)%"
          path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
          protocol: "%PROTOCOL%"
          response_code: "%RESPONSE_CODE%"
          response_flags: "%RESPONSE_FLAGS%"
          bytes_received: "%BYTES_RECEIVED%"
          bytes_sent: "%BYTES_SENT%"
          total_duration: "%DURATION%"
          resp_backend_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
          req_x_forwarded_for: "%REQ(X-FORWARDED-FOR)%"
          user_agent: "%REQ(USER-AGENT)%"
          request_id: "%REQ(X-REQUEST-ID)%"
          authority: "%REQ(:AUTHORITY)%"
          backendHost: "%UPSTREAM_HOST%"
          backendCluster: "%UPSTREAM_CLUSTER%"
EOF

HTTP/2 for in-cluster services

To demonstrate the HTTP/2 routing capabilities, deploy a sample nginx server and configure it to only accept HTTP/2 connections.

  1. Deploy a simple nginx server that is configured to use the HTTP/2 protocol. Note that in this example, the appProtocol on the nginx service is set to http2. Other supported values to configure the HTTP/2 protocol include grpc, grpc-web, or kubernetes.io/h2c.

    kubectl apply -f- <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: nginx-conf
    data:
      nginx.conf: |
        user nginx;
        worker_processes  1;
        events {
          worker_connections  10240;
        }
        http {
          server {
              listen       80 http2;
              server_name  localhost;
              location / {
                root   /usr/share/nginx/html;
                index  index.html index.htm;
            }
          }
        }    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      selector:
        app.kubernetes.io/name: nginx
      ports:
        - protocol: TCP
          port: 8080
          targetPort: http-web-svc
          appProtocol: http2
    --- 
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app.kubernetes.io/name: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:stable
          ports:
            - containerPort: 80
              name: http-web-svc
          volumeMounts:
            - name: nginx-conf
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx.conf
              readOnly: true
      volumes:
      - name: nginx-conf
        configMap:
          name: nginx-conf
          items:
            - key: nginx.conf
              path: nginx.conf
    EOF
  2. Verify that the nginx server is up and running.

    kubectl get pods | grep nginx

    Example output:

    nginx      1/1     Running   0          15s
    
  3. Create an HTTPRoute to expose the nginx server on the Gateway.

    kubectl apply -f- <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: nginx
      namespace: default
    spec:
      parentRefs:
        - name: http
          namespace: kgateway-system
      hostnames:
        - http2.example.com
      rules:
        - backendRefs:
            - name: nginx
              port: 8080
    EOF
  4. Send a request to the nginx server and include the --http2-prior-knowledge option to send an HTTP/2 request to the Gateway. Verify that the request succeeds and that you get back a 200 HTTP response code.

    curl -vik http://$INGRESS_GW_ADDRESS:8080/ -H "host: http2.example.com:8080" --http2-prior-knowledge 
    curl -i localhost:8080/ -H "host: http2.example.com" --http2-prior-knowledge 

    Example output:

    ...
    > GET / HTTP/2
    > Host: http2.example.com:8080
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/2 200 
    HTTP/2 200 
    ...
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
  5. Get the access logs of your gateway proxy.

    kubectl -n kgateway-system logs deployments/http | tail -1 | jq --sort-keys

    Example output:

    {
      "authority": "http2.example.com:8080",
      "backendCluster": "kube_default_nginx_8080",
      "backendHost": "10.0.2.26:80",
      "bytes_received": 0,
      "bytes_sent": 615,
      "method": "GET",
      "path": "/",
      "protocol": "HTTP/2",
      "req_x_forwarded_for": "10.0.15.215",
      "request_id": "c996b154-9299-4f00-b356-8cc66472e613",
      "resp_backend_service_time": "0",
      "response_code": 200,
      "response_flags": "-",
      "start_time": "2025-06-23T16:23:09.615Z",
      "total_duration": 0,
      "user_agent": "curl/8.7.1"
    }
    

HTTP/2 for external services

  1. Create a Backend resource that represents your external service. In this example, you configure a Backend for the https://nghttp2.org/httpbin/ domain. This domain requires requests to be sent with the HTTP/2 protocol.

    kubectl apply -f- <<EOF
    apiVersion: gateway.kgateway.dev/v1alpha1
    kind: Backend
    metadata:
      name: http2
    spec:
      type: Static
      static:
        hosts:
          - host: nghttp2.org
            port: 80
        appProtocol: http2
    EOF
  2. Create an HTTPRoute that routes requests to your Backend.

    kubectl apply -f- <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: http2
      namespace: default
    spec:
      parentRefs:
      - name: http
        namespace: kgateway-system
      hostnames:
        - static.example
      rules:
        - backendRefs:
          - name: http2
            kind: Backend
            group: gateway.kgateway.dev 
          filters:
          - type: URLRewrite
            urlRewrite:
              hostname: nghttp2.org
              path:
               type: ReplacePrefixMatch
               replacePrefixMatch: /httpbin/
    EOF
  3. Send a request to the static.example domain. The request is forwarded to the nghttp2.org/httpbin/ path, which only accepts HTTP/2 requests. Verify that the request succeeds and that you get back a 200 HTTP response code.

    curl -vik http://$INGRESS_GW_ADDRESS:8080/ -H "host: static.example:8080" --http2-prior-knowledge 
    curl -i localhost:8080/ -H "host: static.example" --http2-prior-knowledge 

    Example output:

    ...
    > GET / HTTP/2
    > Host: static.example:8080
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/2 200 
    HTTP/2 200 
    < content-type: text/html; charset=utf-8
    content-type: text/html; charset=utf-8
    < content-length: 9649
    content-length: 9649
    < access-control-allow-origin: *
    access-control-allow-origin: *
    < access-control-allow-credentials: true
    access-control-allow-credentials: true
    < x-backend-header-rtt: 6.191667
    x-backend-header-rtt: 6.191667
    < alt-svc: h3=":443"; ma=3600
    alt-svc: h3=":443"; ma=3600
    < server: envoy
    server: envoy
    < via: 1.1 nghttpx
    via: 1.1 nghttpx
    < x-frame-options: SAMEORIGIN
    x-frame-options: SAMEORIGIN
    < x-xss-protection: 1; mode=block
    x-xss-protection: 1; mode=block
    < x-content-type-options: nosniff
    x-content-type-options: nosniff
    < x-envoy-upstream-service-time: 6504
    x-envoy-upstream-service-time: 6504
    < 
    ...
    
  4. Get the access logs of your gateway proxy.

    kubectl -n kgateway-system logs deployments/http | tail -1 | jq --sort-keys

    Example output:

    {
      "authority": "nghttp2.org",
      "backendCluster": "backend_default_http2_0",
      "backendHost": "139.162.123.134:80",
      "bytes_received": 0,
      "bytes_sent": 9649,
      "method": "GET",
      "path": "/",
      "protocol": "HTTP/2",
      "req_x_forwarded_for": "10.0.9.76",
      "request_id": "6e8baf82-2c2b-40cc-b5c0-d2d2295d3abf",
      "resp_backend_service_time": "313",
      "response_code": 200,
      "response_flags": "-",
      "start_time": "2025-06-23T16:25:22.639Z",
      "total_duration": 314,
      "user_agent": "curl/8.7.1"
    }
    

Cleanup

You can remove the resources that you created in this guide.
kubectl delete httproute nginx
kubectl delete pod nginx
kubectl delete service nginx
kubectl delete configmap nginx-conf
kubectl delete backend http2
kubectl delete httproute http2