Skip to content
If you are interested in trying out Gloo Gateway with the Kubernetes Gateway API, check out Solo Enterprise for kgateway. This version adds enterprise functionality on top of the kgateway open source project.

Stateful session filter

Page as Markdown

Set up strong stickiness for upstream services with the Envoy Stateful Session filter.

About session affinity

Session affinity, also referred to as sticky session, allows you to route requests for a particular session to the same upstream service instance that served the initial request. This setup is particularly useful if you have an upstream service that performs expensive operations and caches the output or data for subsequent requests. With session affinity, you make sure that the expensive operation is performed once and that subsequent requests can be served from the upstream’s cache, which can significantly improve operational cost and response times for your clients.

About the stateful session filter

You can implement strong stickiness for you upstream services by using the Envoy Stateful Session filter. This filter instructs the gateway proxy to pick an upstream service instance that serves the request, encode its address, and to return this value as a header or cookie to the requesting client. If the header or cookie is used in subsequent requests, the gateway proxy forwards all traffic to the instance that served the initial request.

Before you begin

  1. Follow the Get started guide to install Gloo Gateway, set up a gateway resource, and deploy the httpbin sample app.

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

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

Choose between the following options to set up the stateful session filter:

Header-based session affinity

Set up header-based session affinity on your gateway proxy. When a request comes in, the proxy chooses an upstream service instance that serves the request, decodes the instance’s address to base64, and stores the base64-encoded value in a header. If the header is provided in subsequent requests, the gateway proxy forwards traffic to the same instance, thereby establishing session affinity.

  1. Scale the httpbin app up to 2 instances.

    kubectl scale deployment httpbin -n httpbin --replicas=2
  2. Verify that another instance of the httpbin app is created and note the IP addresses of both httpbin instances. In the following example, you have an httpbin instance available at the 10.0.43.175 and 10.0.38.52 IP addresses.

    kubectl get pods -n httpbin -o wide

    Example output

    NAME                      READY   STATUS        RESTARTS   AGE    IP            NODE                          NOMINATED NODE   READINESS GATES
    httpbin-8d557795f-86hzg   3/3     Running       0          54m    10.0.43.175   ip-10-0-34-108.ec2.internal   <none>           <none>
    httpbin-8d557795f-h8ks9   3/3     Running       0          126m   10.0.38.52    ip-10-0-39-74.ec2.internal    <none>           <none>
  3. Create an HttpListenerOption resource that enables header-based session affinity on your gateway proxy. In the following example, when a request reaches the proxy, the proxy chooses an upstream service instance to fulfill the request. The proxy encodes the IP address of the instance to base64 and returns it in the x-session-id header.

    kubectl apply -f- <<EOF
    apiVersion: gateway.solo.io/v1
    kind: HttpListenerOption
    metadata:
      name: session
      namespace: gloo-system
    spec:
      targetRefs:
      - group: gateway.networking.k8s.io
        kind: Gateway
        name: http
        namespace: gloo-system
      options:
        statefulSession:
          headerBased:
            headerName: x-session-id
    EOF
  4. Send a request to the httpbin app. Verify that you get back a 200 HTTP response code and that you see the base64-encoded value of the httpbin instance that served the initial request in the x-session-id header.

    curl -vik http://$INGRESS_GW_ADDRESS:8080/headers \
    -H "host: www.example.com:8080"
    curl -vik localhost:8080/headers \
    -H "host: www.example.com:8080"

    Example output:

    * Request completely sent off
    < HTTP/1.1 200 OK
    HTTP/1.1 200 OK
    ...
    < x-envoy-upstream-service-time: 0
    x-envoy-upstream-service-time: 0
    < x-session-id: MTAuMC4zOC41Mjo4MDgw
    x-session-id: MTAuMC4zOC41Mjo4MDgw
    < server: envoy
    server: envoy
    ...

  5. Optional: Decode the base64-encoded value of the x-session-id header and ensure that this session ID points to one of IP addresses of the httpbin instances that you retrieved earlier.

    echo "MTAuMC4zOC41Mjo4MDgw" | base64 -D

    Example output:

    10.0.38.52:8080%
  6. Send a few more requests to the httpbin app. This time, you include the session ID that was returned in your first request to direct all requests to the httpbin instance that served the initial request.

    for i in {1..10}; do
    curl -vik http://$INGRESS_GW_ADDRESS:8080/headers \
    -H "host: www.example.com:8080" \
    -H "x-session-id: MTAuMC4zOC41Mjo4MDgw"; done
    for i in {1..10}; do
    curl -vik localhost:8080/headers \
    -H "host: www.example.com:8080" \
    -H "x-session-id: MTAuMC4zOC41Mjo4MDgw"; done

  7. Get the logs of the httpbin instance that served the request and verify that all requests were served by the same instance.

    kubectl logs <httpbin-pod> -n httpbin

    Example log entry:

    time="2025-03-05T18:57:18.446" status=200 method="GET" uri="/headers" size_bytes=367 duration_ms=0.05 user_agent="curl/8.7.1" client_ip=10.0.XX.XX:45776

Cookie-based session affinity

Set up cookie-based session affinity on your gateway proxy. When a request comes in, the proxy chooses an upstream service instance that serves the request, decodes the instance’s address to base64, and stores the base64-encoded value in a cookie. If the cookie is provided in subsequent requests, the gateway proxy forwards traffic to the same instance, thereby establishing session affinity.

  1. Scale the httpbin app up to 2 instances.

    kubectl scale deployment httpbin -n httpbin --replicas=2
  2. Verify that another instance of the httpbin app is created and note the IP addresses of both httpbin instances. In the following example, you have an httpbin instance available at the 10.0.43.175 and 10.0.38.52 IP addresses.

    kubectl get pods -n httpbin -o wide

    Example output

    NAME                      READY   STATUS        RESTARTS   AGE    IP            NODE                          NOMINATED NODE   READINESS GATES
    httpbin-8d557795f-86hzg   3/3     Running       0          54m    10.0.43.175   ip-10-0-34-108.ec2.internal   <none>           <none>
    httpbin-8d557795f-h8ks9   3/3     Running       0          126m   10.0.38.52    ip-10-0-39-74.ec2.internal    <none>           <none>
  3. Create an HttpListenerOption to set up cookie-based session affinity on your gateway proxy. In the following example, when a request reaches the proxy, the proxy chooses an upstream service instance to fulfill the request. The proxy encodes the IP address of the instance to base64 and returns it in a statefulsessioncookie cookie. The cookie expires after 60 seconds (TTL).

    kubectl apply -f- <<EOF
    apiVersion: gateway.solo.io/v1
    kind: HttpListenerOption
    metadata:
      name: session
      namespace: gloo-system
    spec:
      targetRefs:
        - group: gateway.networking.k8s.io
          kind: Gateway
          name: http
      options:
        statefulSession:
          cookieBased:
            cookie:
              name: statefulsessioncookie
              path: /
              ttl: 60s
    EOF
  4. Send a request to the httpbin app and verify that you see the statefulsessioncookie cookie in the set-cookie header of your response. The -c option stores the cookie in a local file on your machine so that you can use it in subsequent requests.

    curl -i -c cookie-jar -k http://$INGRESS_GW_ADDRESS:8080/headers \
    -H "host: www.example.com:8080"
    curl -i -c cookie-jar -k localhost:8080/headers \
    -H "host: www.example.com:8080"

    Example output:

    HTTP/1.1 200 OK
    ...
    set-cookie: statefulsessioncookie="ChAxMC4wLjQzLjE3NTo4MDgwELerCQ=="; Max-Age=60; Path=/; HttpOnly
    server: envoy
    
    {
      "headers": {
        "Accept": [
          "*/*"
        ],
        "Host": [
          "www.example.com:8080"
        ],
        "User-Agent": [
          "curl/8.7.1"
        ],
        "X-Envoy-Expected-Rq-Timeout-Ms": [
          "15000"
        ],
        "X-Forwarded-Proto": [
          "http"
        ],
        "X-Request-Id": [
          "f1341372-b7ab-4ec2-9d7a-48bf05265c0b"
        ]
      }
    }

  5. Optional: Decode the base64-encoded value that was returned in the statefulsessioncookie cookie and ensure that this IP address matches one of IP addresses of the httpbin instances that you retrieved earlier.

    echo "ChAxMC4wLjQzLjE3NTo4MDgwELerCQ==" | base64 -D

    Example output:

    10.0.43.175:8080??
  6. Send a few more requests to the httpbin app. This time, you include the cookie that you stored in the local file by using the -b option. Make sure to send these requests within the 60 second cookie validity period.

    for i in {1..10}; do
    curl -i -b cookie-jar -k http://$INGRESS_GW_ADDRESS:8080/headers \
    -H "host: www.example.com:8080"; done
    for i in {1..10}; do
    curl -i -b cookie-jar -k localhost:8080/headers \
    -H "host: www.example.com:8080"; done

  7. Get the logs of the httpbin instance that served the initial request. Verify that all subsequent requests were also served by the same instance.

    kubectl logs <httpbin-pod> -n httpbin

    Example log entry:

    time="2025-03-05T18:57:18.446" status=200 method="GET" uri="/headers" size_bytes=367 duration_ms=0.05 user_agent="curl/8.7.1" client_ip=10.0.XX.XX:45776

Cleanup

You can optionally remove the resources that you created in this guide.

kubectl delete httplisteneroption session -n gloo-system
kubectl scale deployment httpbin -n httpbin --replicas=1