For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Session persistence
Verified : Automated tests passed for: Kubernetes resources: The Kubernetes resources on this page were checked against their API schemas. End-to-end run: Every step ran in a fresh test environment and the results were checked.Maintain client-to-backend affinity with cookie or header-based session persistence.
Ensure that traffic from a client is always routed to the same backend instance for the duration of a session.
Warning
Session persistence is experimental.
- BackendConfigPolicy: The
sessionPersistencefield is an experimental kgateway API and is subject to breaking changes. The field does not require the experimental channel of the Kubernetes Gateway API. - HTTPRoute: The
sessionPersistencefield is part of the experimental channel of the Kubernetes Gateway API, which deprecates the field in version 1.7.
About
Session persistence, also known as “strong” session affinity or sticky sessions, ensures that traffic is always routed to the same backend instance for the duration of the session, which can improve request latency and the user experience.
In session persistence, a client directly provides information, such as a header or a cookie, that the gateway proxy uses as a reference to direct traffic to a specific backend instance. The persistent client request bypasses the load balancing algorithm of the proxy and is routed directly to the backend instance that it previously established a session with.
Cookie: When you use cookie-based persistence, a client sends a request to your app, and receives a set-cookie response header. The client then reuses that value in a cookie request header in subsequent requests. The gateway proxy uses this cookie to maintain a persistent connection to a single backend instance on behalf of the client.
Header: When you use header-based persistence, a client sends a request to your app, and receives an HTTP response header. The client then reuses that value in the same header in subsequent HTTP requests. The gateway proxy uses this header to maintain a persistent connection to a single backend instance on behalf of the client.
You can configure session persistence in one of the following ways.
- HTTPRoute: Set the
sessionPersistencefield of an HTTPRoute rule. The settings apply to the requests that the rule matches. - BackendConfigPolicy: Set the
sessionPersistencefield of a BackendConfigPolicy that targets a Service or a Backend. The policy applies to the HTTPRoutes and GRPCRoutes that route to the backend. It has no effect on TCPRoutes or TLSRoutes. If a route splits traffic between backends, the policy pins a client only to an instance of the backend that the split selects.
Note
The Kubernetes Gateway API version 1.7 deprecates the sessionPersistence field of an HTTPRoute rule in favor of session persistence on the backend. The field still works in this version. If a backend has a BackendConfigPolicy with session persistence, the policy takes precedence over the sessionPersistence field of a route rule, and the route reports the SessionPersistenceOverridden reason on its kgateway.dev/Programmed condition.
Session persistence versus session affinity
In session persistence, the backend instance is encoded in a cookie or header and affinity can be maintained for as long as the backend instance is available. This makes session persistence more reliable than session affinity through consistent hashing, or “weak” session affinity, in which affinity might be lost when the set of backend instances changes.
However, note that session persistence and session affinity can functionally work together in your kgateway setup. If you define both session persistence and session affinity through consistent hashing, the gateway proxy makes the following routing decisions for incoming requests:
- If the request contains a session persistence identity in a cookie or header, route the request directly to the backend instance that it previously established a session with.
- If no session persistence identity is present, load balance the request according to the defined session affinity configuration, along with any load balancing configuration.
For more information about session persistence, see the Kubernetes Gateway API enhancement doc.
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
The requirements depend on the option that you use.
HTTPRoute: Install the experimental channel of the Kubernetes Gateway API, because the
sessionPersistencefield of an HTTPRoute is not part of the standard channel. kgateway ignores the field if you disable experimental Gateway API features. For more information, see Experimental Gateway API features.kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.7.0/experimental-install.yamlIf your cluster already has the standard channel CRDs, this command can fail with the error
Installing experimental CRDs on top of standard channel CRDs is prohibited by default. Gateway API version 1.5 and later includes asafe-upgrades.gateway.networking.k8s.ioValidatingAdmissionPolicy that blocks this switch. To continue, delete the binding of the policy, and then apply the experimental bundle again. The bundle re-creates the policy and its binding.kubectl delete validatingadmissionpolicybinding safe-upgrades.gateway.networking.k8s.io kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.7.0/experimental-install.yamlBackendConfigPolicy: You do not need the experimental channel of the Kubernetes Gateway API. The option also works if you disable experimental Gateway API features.
Configure session persistence
Define session persistence based on either a cookie or a header in the sessionPersistence section of a BackendConfigPolicy. The following example defines cookie-based session persistence for the httpbin service.
apiVersion: gateway.kgateway.dev/v1alpha1
kind: BackendConfigPolicy
metadata:
name: session-persistence
namespace: httpbin
spec:
targetRefs:
- name: httpbin
group: ""
kind: Service
sessionPersistence:
type: Cookie
absoluteTimeout: 30m
cookie:
name: Session-A
lifetimeType: PermanentTo use a header instead, set the type to Header and the name of the header.
sessionPersistence:
type: Header
header:
name: Session-BReview the following table to understand this configuration.
| Setting | Description |
|---|---|
targetRefs | The Service or Backend that the policy applies to. |
type | The request property, either Header or Cookie, to base session persistence on. Defaults to Cookie. |
cookie.name | The name of the cookie that the proxy sends in response to the client and that the client reuses in subsequent requests. If you do not set a name, a unique name such as kgw-sp-1cccccdd2c8865e2 is generated for each backend. To prevent unpredictable behavior or rejection, do not reuse cookie names. Do not use the same name for backends in the same traffic split, because clients lose their session whenever the split switches backends. |
cookie.path | The Path attribute of the cookie. Defaults to /. |
cookie.lifetimeType | Whether the cookie has a Permanent or a Session-based lifetime. A permanent cookie persists until its expiry time, while a session cookie is deleted when the current session ends. Defaults to Session. When set to Permanent, absoluteTimeout is required. |
header.name | The name of the header that the proxy sends in response to the client. The client must include this header in subsequent requests to maintain the session. Required if the type is Header. |
absoluteTimeout | The absolute timeout of the persistent session. Once the duration has elapsed, the session becomes invalid. Only cookies with the Permanent lifetime support this field. For session cookies and headers, the field has no effect. |
Note
If you set an absoluteTimeout that has no effect, the BackendConfigPolicy is still accepted, but its Accepted condition has the PartiallyValid reason and the following message: spec.sessionPersistence.absoluteTimeout only has an effect for Cookie sessions with spec.sessionPersistence.cookie.lifetimeType Permanent. Session persistence has no effect on Backends of the dynamic forward proxy type, and the policy reports this in the same way.
Verify the session persistence configuration
To try out session persistence, you can follow these steps to define cookie-based session persistence for the httpbin sample app.
Scale the httpbin app up to two instances.
kubectl scale deployment httpbin -n httpbin --replicas=2Verify that another instance of the httpbin app is created.
kubectl get pods -n httpbinExample output:
NAME READY STATUS RESTARTS AGE httpbin-8d557795f-86hzg 3/3 Running 0 54s httpbin-8d557795f-h8ks9 3/3 Running 0 26mConfigure session persistence for the httpbin app. These settings instruct the gateway proxy to create a persistent session cookie called
Session-Afor any new client connections to the httpbin app. Each created session has an absolute timeout of 30 minutes, and the cookie persists for that time.Create a BackendConfigPolicy for the httpbin service with the
sessionPersistencesection.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: BackendConfigPolicy metadata: name: session-persistence namespace: httpbin spec: targetRefs: - name: httpbin group: "" kind: Service sessionPersistence: type: Cookie absoluteTimeout: 30m cookie: name: Session-A lifetimeType: Permanent EOFSend a request to the httpbin app and verify that you see the
Session-Acookie in theset-cookieheader of your response. The-coption stores the cookie in a local file on your machine so that you can use it in subsequent requests.curl -i -c cookie-jar http://$INGRESS_GW_ADDRESS:8080/headers \ -H "host: www.example.com:8080"Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 date: Wed, 28 Oct 2026 02:21:26 GMT content-length: 446 x-envoy-upstream-service-time: 0 set-cookie: Session-A="Cg8xMC4yNDQuMC4zOjgwODAQrsaF1wY="; Max-Age=1800; Path=/; HttpOnly server: envoy ...Repeat the request a few more times. Include the cookie that you stored in the local file by using the
-boption. Make sure to send these requests within the 30 minute cookie validity period.for i in {1..10}; do curl -i -b cookie-jar http://$INGRESS_GW_ADDRESS:8080/headers \ -H "host: www.example.com:8080"; doneCheck the logs of each httpbin instance. Verify that only one of the instances served all 10 of the subsequent requests that you made.
kubectl logs -n httpbin <httpbin-pod>Example output for one pod, in which the first request (timestamp at
02:21:26) and all 10 subsequent requests (timestamps at02:21:27) are served by the same instance:... time="2026-10-28T02:21:26.3729" status=200 method="GET" uri="/headers" size_bytes=446 duration_ms=0.03 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:21:27.4003" status=200 method="GET" uri="/headers" size_bytes=528 duration_ms=0.04 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:21:27.4155" status=200 method="GET" uri="/headers" size_bytes=528 duration_ms=0.02 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:21:27.428" status=200 method="GET" uri="/headers" size_bytes=528 duration_ms=0.01 user_agent="curl/8.7.1" client_ip=10.244.0.87 ...Optional: Verify the permanence of the session cookie by restarting the http gateway. The session cookie persists after the gateway restarts due to the
cookie.lifetimeType: Permanentsetting.Restart the http gateway proxy.
kubectl rollout restart deploy/http -n kgateway-systemVerify that the gateway proxy pod is running.
kubectl get po -n kgateway-system -l gateway.networking.k8s.io/gateway-name=httpExample output:
NAME READY STATUS RESTARTS AGE http-7dd94b74-k26j6 3/3 Running 0 8sGet the new external address that is assigned to the gateway, or restart local port forwarding.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSRepeat the same request with the session cookie. Make sure to send this request within the 30 minute cookie validity period.
for i in {1..10}; do curl -i -b cookie-jar http://$INGRESS_GW_ADDRESS:8080/headers \ -H "host: www.example.com:8080"; doneCheck the logs again of the httpbin instance that served the previous requests. Verify that your most recent request continued to be served by the same instance, even after the http gateway restarted.
kubectl logs -n httpbin <httpbin-pod>Example output for one pod, in which all 10 new requests (timestamps at
02:22:54) are served after the previous requests (timestamps at02:21:27):... time="2026-10-28T02:21:27.5063" status=200 method="GET" uri="/headers" size_bytes=528 duration_ms=0.01 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:22:54.33" status=200 method="GET" uri="/headers" size_bytes=529 duration_ms=0.12 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:22:54.3502" status=200 method="GET" uri="/headers" size_bytes=529 duration_ms=0.04 user_agent="curl/8.7.1" client_ip=10.244.0.87 time="2026-10-28T02:22:54.3639" status=200 method="GET" uri="/headers" size_bytes=529 duration_ms=0.04 user_agent="curl/8.7.1" client_ip=10.244.0.87 ...
Cleanup
You can remove the resources that you created in this guide.Scale the httpbin app back down.
kubectl scale deployment httpbin -n httpbin --replicas=1Remove the session persistence configuration.
Delete the BackendConfigPolicy.
kubectl delete backendconfigpolicy session-persistence -n httpbin