For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
HTTP protocol upgrades
Allow WebSocket, CONNECT, and other HTTP protocol upgrades on a listener or a route.
About HTTP protocol upgrades
An HTTP upgrade lets a client switch an ordinary HTTP request onto another protocol over the same connection. The most common use case includes WebSocket upgrades or CONNECT requests. In a CONNECT request, instead of upgrading a request in place, the client asks the proxy to open a tunnel to a destination, through which it can send any protocol as raw bytes.
By default, the gateway proxy rejects upgrade requests from clients. To enable upgrades, you can create one of the following resources.
| Resource | Applied to | Field | What it does |
|---|---|---|---|
| ListenerPolicy | Gateway listener | spec.default.httpSettings.upgradeConfig.enabledUpgrades | Lists the Upgrade header values the listener accepts. Needs at least one entry. |
| TrafficPolicy | Gateway, HTTPRoute, or ListenerSet | spec.httpUpgrade | Configures upgrades for the Gateway, HTTPRoute, or ListenerSet that the policy targets, and is the only place CONNECT termination can be set. |
Warning
After an upgrade is established, the tunneled payload is not inspected by HTTP filters. Authenticate and authorize the initial upgrade request, enable upgrades only for clients you trust, and do not enable request buffering on a route that carries upgrades.
Considerations
CONNECTtermination is per route. You cannot configureCONNECTtermination on a listener by using a ListenerPolicy. Always use the TrafficPolicy resource instead.- Terminating
CONNECTrequests forwards raw bytes. If you configure TLS for the selected backend, those bytes are wrapped in a separate upstream TLS session. Leave backend TLS disabled when the payload must reach the backend unchanged. - You cannot combine upgrades with request buffering. A TrafficPolicy that sets both
httpUpgradeandbufferis rejected, unless thebuffersetting disables buffering. - WebSocket over HTTP/2 needs an additional listener setting. Clients such as Firefox open WebSocket connections over HTTP/2 with Extended CONNECT requests that the listener rejects by default. Allowing Extended CONNECT requests is a separate setting from the upgrade configuration on this page. You need set the
allowConnectfield on the listener’shttp2ProtocolOptions, and enable Websocket upgrades on the gateway listener or route. Envoy converts an accepted Extended CONNECT request into a regularwebsocketupgrade internally, so it still has to accept the same upgrade token as any other WebSocket request. For more information, see WebSocket over HTTP/2.
Before you begin
-
Follow the Get started guide to install kgateway.
-
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
-
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
Enable upgrades for a listener
Configure a gateway listener to accept protocol upgrade requests.
-
Send an upgrade request through the gateway proxy, and confirm that it is rejected with a 403 HTTP response. By default, the gateway proxy does not allow protocol upgrades.
curl -v --max-time 5 \ -H "Host: www.example.com" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ http://$INGRESS_GW_ADDRESS:8080/getExample output:
< HTTP/1.1 403 Forbidden < content-length: 0 < server: envoy < connection: close -
Create a ListenerPolicy that lists the upgrade header values that you want the gateway proxy to accept.
kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: ListenerPolicy metadata: name: enable-upgrades namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http default: httpSettings: upgradeConfig: enabledUpgrades: - websocket EOFField Description enabledUpgradesThe upgrade tokens that the listener accepts, such as websocketorCONNECT. Tokens are case-insensitive. List at least one. EnablingCONNECThere proxies CONNECT requests upstream without terminating them. -
Send the same upgrade request again, and confirm that the gateway proxy now forwards it instead of rejecting it. Note that the sample httpbin app does not implement WebSocket, so it never completes the handshake with a
101 Switching Protocolsresponse. Instead, the gateway proxy now accepts the upgrade and holds the connection open, waiting on the httpbin backend, until curl times out after 5 seconds. Getting a timeout instead of a403is the proof that the gateway proxy forwarded the request.curl -v --max-time 5 \ -H "Host: www.example.com" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ http://$INGRESS_GW_ADDRESS:8080/getExample output:
* Request completely sent off * Operation timed out after 5001 milliseconds with 0 bytes received
Enable WebSocket on a route
Use the TrafficPolicy resource instead of a ListenerPolicy when you want WebSocket enabled for only one route rather than every route on the listener. The route-level setting takes precedence over the listener-level setting, so a route can accept an upgrade that the listener does not list.
The TrafficPolicy resource must be in the same namespace as the route that it targets. You can also choose to target a Gateway or ListenerSet instead.
-
If you followed the Enable upgrades for a listener guide, remove the ListenerPolicy that you created.
kubectl delete listenerpolicy enable-upgrades -n kgateway-system -
Create the TrafficPolicy resource that enables WebSocket upgrades on the httpbin route.
kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: websocket-upgrade namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin httpUpgrade: - type: websocket EOFField Description httpUpgrade[].typeThe upgrade token, such as websocket,CONNECT, orspdy/3.1. Required, and case-insensitive. Do not list the same token twice, including variants that differ only in letter case. Up to 16 entries. -
Send an upgrade request to the httpbin route. Because the sample httpbin app does not implement WebSocket, the connection times out instead of completing a
101 Switching Protocolshandshake.curl -v --max-time 5 \ -H "Host: www.example.com" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ http://$INGRESS_GW_ADDRESS:8080/getExample output:
* Request completely sent off * Operation timed out after 5001 milliseconds with 0 bytes received -
Delete the TrafficPolicy, and send the same request again. Verify that this time, the gateway proxy rejects the request with a 403 HTTP response, because neither the gateway proxy nor the route accept WebSocket upgrades.
kubectl delete TrafficPolicy websocket-upgrade -n httpbincurl -v --max-time 5 \ -H "Host: www.example.com" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ http://$INGRESS_GW_ADDRESS:8080/getExample output:
< HTTP/1.1 403 Forbidden < content-length: 0 < server: envoy < connection: close
Terminate CONNECT requests on a route
Use the TrafficPolicy resource to allow incoming CONNECT upgrades for a specific route and to terminate them at the gateway proxy. When the gateway proxy terminates a CONNECT request, the proxy responds to the client. Then, the proxy forwards the client’s subsequent bytes, an ordinary HTTP request, as a separate connection to the backend.
Note
CONNECT upgrades must be configured with the TrafficPolicy resource. You cannot use a ListenerPolicy to configure CONNECT upgrades for a gateway listener.
-
Create an HTTPRoute for
CONNECTrequests. ACONNECTrequest carries a host and port instead of a path, so it matches a route rule only if the rule matches onmethod: CONNECTwithout a path.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-connect namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - connect.example rules: - name: connect matches: - method: CONNECT backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that terminates
CONNECTrequests on that HTTPRoute rule.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: connect-termination namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin-connect sectionName: connect httpUpgrade: - type: CONNECT connect: terminate: true EOFField Description targetRefs[].sectionNameThe name of the HTTPRoute rule to apply the policy to. httpUpgrade[].connect.terminateTerminates the CONNECTrequest at the gateway proxy and forwards the payload to the backend as raw TCP data. When omitted orfalse, the gateway proxy forwards theCONNECTrequest to the backend without terminating it. Valid only whentypeisCONNECT. -
Send a request through the tunnel. The
-pand-xoptions open aCONNECTtunnel between curl and the gateway proxy. The proxy terminates theCONNECTrequest and forwards the client’s subsequent bytes, an ordinary HTTP request, to the httpbin app as a separate connection.Verify that you see two
200 OKresponses in the output. The first response is the gateway proxy accepting theCONNECTrequest and establishing the tunnel. The second is the httpbin app responding to theGET /headersrequest that curl sent over that tunnel.curl -v -p -x http://$INGRESS_GW_ADDRESS:8080 http://connect.example:8000/headersExample output:
< HTTP/1.1 200 OK < server: envoy < * CONNECT phase completed * CONNECT tunnel established, response 200 > GET /headers HTTP/1.1 > Host: connect.example:8000 > User-Agent: curl/8.7.1 > Accept: */* > * Request completely sent off < HTTP/1.1 200 OK < Access-Control-Allow-Credentials: true < Access-Control-Allow-Origin: * < Content-Type: application/json; encoding=utf-8 < Content-Length: 153 < { "headers": { "Accept": [ "*/*" ], "Host": [ "connect.example:8000" ], "User-Agent": [ "curl/8.7.1" ] } } -
Remove the
terminatesetting, and send the same request again. Without termination, the gateway proxy forwards the rawCONNECTrequest to httpbin instead of terminating it. The httpbin app is a plain HTTP server with noCONNECTsupport, so the request fails.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: connect-termination namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin-connect sectionName: connect httpUpgrade: - type: CONNECT EOFcurl -v -p -x http://$INGRESS_GW_ADDRESS:8080 http://connect.example:8000/headersExample output:
< HTTP/1.1 404 Not Found < access-control-allow-credentials: true < access-control-allow-origin: * < content-type: text/plain; charset=utf-8 < x-content-type-options: nosniff < content-length: 19 < server: envoy < connection: close < * CONNECT tunnel failed, response 404 * Closing connection curl: (56) CONNECT tunnel failed, response 404
Cleanup
You can remove the resources that you created in this guide.kubectl delete listenerpolicy enable-upgrades -n kgateway-system --ignore-not-found
kubectl delete TrafficPolicy websocket-upgrade connect-termination -n httpbin --ignore-not-found
kubectl delete httproute httpbin-connect -n httpbin --ignore-not-found