HTTP/2 PING frames are a surprisingly effective way to keep idle connections alive, and they work by sending a small, unsolicited message that the other side must acknowledge, proving the connection is still viable.
Let’s see this in action. Imagine you have a client making a request to a server.
:method: GET
:path: /
:scheme: https
:authority: example.com
# ... request headers ...
The server processes this and sends a response. If the client goes quiet for a while, the network might decide the connection is stale and tear it down. To prevent this, the client (or server) can periodically send a PING frame.
# PING Frame (type=0x00)
# Payload: 8 bytes of arbitrary data
# Example payload: 0xdeadbeef00000001
When the receiving endpoint gets a PING frame, it must respond with a PONG frame, echoing the exact payload of the PING.
# PONG Frame (type=0x01)
# Payload: 8 bytes of arbitrary data (must match PING payload)
# Example payload: 0xdeadbeef00000001
This exchange confirms that both ends of the connection are still reachable and the network path between them is open. It’s a lightweight heartbeat.
The problem this solves is the "idle connection timeout." Many network devices, like load balancers, firewalls, and even intermediate routers, have configurable timeouts for idle TCP connections. When a connection has no traffic for a certain period, these devices will silently drop it to conserve resources. For applications that maintain long-lived HTTP/2 connections, especially for things like server-sent events or persistent API calls, this can lead to unexpected connection failures and a degraded user experience. PING frames provide a way to "poke" the connection periodically, resetting these idle timers on all intervening devices without needing to send actual application data.
Internally, HTTP/2 frames are multiplexed over a single TCP connection. When a PING frame is sent, it’s just another stream of bytes within that TCP flow. The HTTP/2 layer on the receiving end intercepts it, processes it, and immediately generates the PONG response. The key is that this happens at the HTTP/2 layer itself, below the application data. This means you don’t need application-level logic to send keep-alives; the HTTP/2 library or server handles it.
The primary lever you control is the interval at which these PING frames are sent. This is not a standard HTTP/2 setting but rather a configuration option in your specific HTTP/2 client or server implementation. For example, in some client libraries, you might configure a ping_interval of 30 seconds. This tells the client to send a PING frame every 30 seconds if no other HTTP/2 frames have been sent or received on that connection during that interval. Similarly, servers can be configured to send PINGs. The crucial point is that the PING frame must contain an identifier that the responder echoes back. This ensures that the PONG is a direct response to a specific PING, preventing confusion if multiple PINGs are in flight or if some frames are lost.
The exact mechanism for configuring PING frames varies by implementation. For instance, in Envoy Proxy, you might configure connection keep-alives for upstream HTTP/2 clusters, which indirectly leads to PING frames being sent. Nginx has http2_ping_interval and http2_ping_timeout directives. Client libraries like h2 in Python or http2-go in Go offer explicit ping() or ping_interval parameters. The important detail is that the PING payload is a sequence of arbitrary bytes, often 8 bytes in length, that must be echoed back in the PONG. This isn’t just a simple "are you there?" signal; it’s a verifiable round-trip confirmation.
Most people assume that if an HTTP/2 connection is idle, no frames are being sent. However, the specification allows for PING frames to be sent even if the connection is busy with other streams. This is a critical distinction: PING frames don’t require a lull in application traffic. A client or server can, and often should, interleave PING frames with regular data frames to ensure that idle periods are detected and acted upon by network intermediaries, even if the application itself is actively using the connection for other requests and responses.
The next thing you’ll likely encounter is managing the PING frame timeout, ensuring that if a PONG isn’t received within a reasonable time, the connection is considered broken.