Skip to main content
Datum’s Application Load Balancer (ALB) runs on Datum’s edge, the proxies that sit between your clients and your origin. This page documents how Datum’s edge handles requests and responses, including forwarded headers, client IP addresses, supported protocols, origin connections, and timeouts.

Find the client’s IP address

The edge adds the client’s IP address to two request headers before your origin sees the request: Use the last entry of X-Forwarded-For, or X-Envoy-External-Address. Every entry before the last one in X-Forwarded-For came from the client and can be anything.
Many frameworks take the first entry of X-Forwarded-For by default. Behind Datum, that is whatever the client chose to send. Before you use the address for rate limits, audit logs, or access rules, configure your framework to trust exactly one proxy hop, so it reads the last entry. In Express, for example, that is app.set('trust proxy', 1). app.set('trust proxy', true) does the opposite and reads the first entry.This assumes the edge is the only proxy in front of your app. If your origin runs behind its own proxy or platform router, which adds its own entry, trust one more hop for each. If you put another proxy or CDN in front of Datum, the client’s address is no longer the last entry.

Headers added to requests

The edge doesn’t add X-Forwarded-Port. Where your origin finds the hostname the client used depends on the backend. See The Host header.

Headers passed through unchanged

The edge forwards these headers exactly as the client sent them, so don’t trust them for anything that matters:
  • X-Real-IP
  • True-Client-IP
  • Forwarded
  • X-Forwarded-Host, on networkService backends
  • X-Forwarded-Port

Response headers

The edge passes your origin’s response headers to the client unchanged, including every Set-Cookie header, CORS headers such as Access-Control-Allow-Origin, and caching headers such as Cache-Control. It adds one header: The edge doesn’t remove headers such as Server or X-Powered-By, and it doesn’t add security headers such as Strict-Transport-Security. Set or remove those in your origin.

How long a response can stay open

By default, an HTTP response must finish within about 15 seconds of the request reaching the edge. If it doesn’t, the edge ends it:
  • If your origin hasn’t started answering by then, the client gets a 504 Gateway Timeout from the edge.
  • If your origin has started answering, the edge cuts the response off at about 15 seconds, even while data is still flowing. This includes streaming responses such as Server-Sent Events (SSE), LLM token streams, and long MCP responses.
WebSockets are the exception. See WebSockets.

Raise the limit

HTTPProxy has no timeout field. Set the limit with a BackendTrafficPolicy, up to 1h, attached to the Gateway that Datum creates for your proxy. The Gateway has the same name as the HTTPProxy.
Don’t attach the policy to the proxy’s HTTPRoute. Datum attaches its own BackendTrafficPolicy to that route when the proxy has a network service origin, a load balancing algorithm, or health checks, and a route accepts only one. The older policy wins, and the other reports Accepted: False with reason Conflicted. Either your timeout has no effect, or the proxy loses its algorithm and health checks while it still reports Programmed. A policy on the Gateway merges with Datum’s, so both apply.
requestTimeout limits the whole response, from the request arriving to the last byte, so set it longer than your longest stream or download. A larger value is rejected with duration must be less than or equal to 1h0m0s. A Gateway accepts only one BackendTrafficPolicy. If you’ve already attached one to it, such as for rate limiting, add the timeout block to that policy instead of creating a second one. If you added the timeout to an existing policy, remove its timeout block and apply the policy again. If you created a dedicated policy with the example above, delete it to restore the default:

WebSockets

The 15-second limit doesn’t end a WebSocket. In testing, an upgraded connection stayed open while messages flowed, and through idle gaps of 2 minutes over 6 minutes in total. No idle limit has been measured.

gRPC

In testing, bidirectional gRPC streams of 25 and 45 seconds weren’t cut at 15 seconds. This isn’t a documented guarantee. If your calls can run longer than 15 seconds, raise the limit.

Plain HTTP

The load balancer listens on both HTTP (port 80) and HTTPS (port 443). Whether plain HTTP is redirected depends on how the load balancer was set up:
  • datumctl alb create turns on Force HTTPS, which redirects plain HTTP to HTTPS. Turn it off or on with datumctl alb update NAME --no-force-https or --force-https.
  • datumctl compute deploy --http-port creates a load balancer that serves plain HTTP with no redirect. To add the redirect, run datumctl alb update NAME --force-https.
  • An HTTPProxy you write yourself serves plain HTTP like any other request, with X-Forwarded-Proto: http. To redirect it, add the rule in HTTP to HTTPS redirect.

Client protocols

Whatever protocol the client uses, the edge makes its own connection to your origin.

How the edge connects to your origin

HTTP or HTTPS

The backend type decides how the edge connects:

The Host header

To send a different Host, see Host header override.

Persistent connections

The edge keeps connections to your origin open and reuses them for later requests. Requests arrive over many connections at once, so don’t rely on one client’s requests sharing an origin connection.

gRPC to your origin

gRPC needs HTTP/2 all the way to the origin.
  • endpoint: https://...: works when your origin offers HTTP/2 over TLS, as gRPC servers do.
  • networkService: the port must use h2c. Without it, the edge sends HTTP/1.1, which is incompatible with standard gRPC, and every call fails with 502 and upstream_reset_before_response_started{protocol_error}.
To set up the port:
  • With datumctl compute deploy, add --http2. See Compute and ALB.
  • On a network service, use --port=name=grpc,port=9090,app-protocol=h2c.
  • In a manifest, set appProtocol: kubernetes.io/h2c on the port:
With h2c set, every request on that port reaches your instances as HTTP/2, even when the client sent HTTP/1.1. Most servers accept WebSockets only over HTTP/1.1, so serve WebSockets on a different port.
Last modified on November 6, 2026