skip to content

HTTP/2 over cleartext, known as h2c, is defined in the specification but almost never seen on the public web. Why is that, and where does cleartext HTTP/2 still get used?

level: seniorimportance: nice to knowfreq 22%

answer

  1. h2c legal in the RFC, no browser implements it
  2. no TLS -> no ALPN -> Upgrade 101 (deprecated) or prior knowledge
  3. preface PRI * HTTP/2.0 ... SM
  4. middleboxes mangle cleartext binary framing
  5. lives on: gRPC in-cluster, TLS-terminating proxy to origin

basics

~20 s

No browser implements h2c, so public traffic never uses it. Without TLS there is no ALPN, so h2c needs prior knowledge or an Upgrade handshake, and middleboxes mangle unfamiliar cleartext traffic. It survives inside trusted networks: gRPC between services and proxy-to-origin hops.

solid answer

~50 s

The specification permits HTTP/2 without TLS (h2c), but three things killed it publicly: 1. **No browser ever implemented it.** Chrome and Firefox require TLS for HTTP/2, so `http://` URLs are HTTP/1.1, full stop. That alone removes the entire public use case. 2. **Negotiation is awkward.** ALPN lives in the TLS handshake, so cleartext has to use either an `Upgrade: h2c` request answered with `101 Switching Protocols` - costing a round trip and deprecated in RFC 9113 - or **prior knowledge**, where the client simply sends the HTTP/2 connection preface and assumes the server speaks it. 3. **Middlebox interference.** Cleartext traffic gets inspected and rewritten by proxies and transparent caches that only understand HTTP/1.1. Encryption is what made HTTP/2 deployable at all. Where it lives today: **inside trusted networks**. gRPC mandates HTTP/2 and very often runs h2c with prior knowledge between services in a cluster; a sidecar or load balancer terminates TLS and forwards h2c to the local application; internal admin and metrics endpoints do the same.

code

bash · 7 lines
bash
# prior knowledge: assume the server speaks h2c, send the preface immediately
curl -sI --http2-prior-knowledge http://service.internal:8080/health | head -1
# HTTP/2 200

# against an HTTP/1.1-only server the preface is rejected outright
curl -s --http2-prior-knowledge http://legacy.internal:8080/ -o /dev/null -w '%{http_code}\n'
# 400

go deeper

for a junior

Know that browsers only do HTTP/2 over HTTPS, so cleartext HTTP/2 is not something you meet on the public web.

for a middle

Explain that no TLS means no ALPN, leaving the deprecated Upgrade handshake or prior knowledge, and name gRPC as the common cleartext user.

for a senior

Add the middlebox argument for why encryption made HTTP/2 deployable, and the operational trap of an HTTP/1.1 backend leg behind an HTTP/2 edge.

for a principal

Treat it as a trust-boundary decision: cleartext h2 inside a controlled network versus mutual TLS via a mesh, and set the organisational default for backend protocol configuration.

## What h2c is `h2c` is the protocol identifier for HTTP/2 over cleartext TCP. The HTTP/2 specification defines it, so a conforming implementation may serve HTTP/2 with no TLS at all. It is a real, usable protocol - it simply lost the public web. ## Why it lost **Browsers refused.** During HTTP/2 standardisation there was a long argument about mandatory encryption. The RFC did not require TLS, but the browser vendors independently decided to implement HTTP/2 only over TLS. Since browsers are the overwhelming majority of public HTTP clients, that decision settled it: on the public web, HTTP/2 means HTTPS. **Negotiation is clumsy without TLS.** Over TLS, ALPN selects the protocol for free inside a handshake you were already doing. Cleartext has neither, so HTTP/2 defined two mechanisms: - **Upgrade**: the client sends an HTTP/1.1 request carrying `Connection: Upgrade, HTTP2-Settings`, `Upgrade: h2c` and a base64 `HTTP2-Settings` header; the server may answer `101 Switching Protocols` and the connection becomes HTTP/2. It costs a round trip on the first request and requires every intermediary in the path to pass Upgrade through correctly. RFC 9113 deprecated it, largely for lack of use. - **Prior knowledge**: the client simply opens TCP and sends the HTTP/2 connection preface (`PRI * HTTP/2.0` followed by `SM` and a SETTINGS frame). No negotiation at all - the client must already know the server speaks h2c, by configuration. This is what actually gets used. **Middleboxes.** A great deal of deployed infrastructure - transparent proxies, corporate filters, old load balancers - parses and rewrites cleartext HTTP/1.1. Feed it binary HTTP/2 frames and it may drop, hang or corrupt the connection, and there is no way to detect this in advance. TLS made HTTP/2 deployable precisely because encrypted bytes are opaque and get forwarded untouched. The same argument later drove QUIC's encryption of nearly its entire header. ## Where cleartext HTTP/2 is genuinely common Inside networks you control, where there are no browsers and no hostile middleboxes: - **gRPC.** gRPC requires HTTP/2 for multiplexed concurrent RPCs and bidirectional streaming. Service-to-service gRPC inside a cluster very often runs h2c with prior knowledge, because both peers are configured together and mutual TLS may be handled by a sidecar or by the network layer instead. - **TLS-terminating front proxies.** An edge load balancer or ingress terminates TLS and forwards to the application over h2c on loopback or the pod network. This is a common misconfiguration point too: many ingress controllers default to HTTP/1.1 on the backend leg, which silently breaks gRPC and quietly discards HTTP/2's benefits. - **Internal APIs, metrics and admin endpoints** in trusted environments. ## What to watch for operationally - The backend protocol is a deliberate setting in most proxies (nginx grpc_pass and upstream http2 options, Envoy's explicit HTTP/2 upstream protocol option, Kubernetes ingress annotations). Assume HTTP/1.1 on the back leg unless you configured otherwise, and verify. - Prior-knowledge h2c fails ugly against an HTTP/1.1-only server: the server sees the `PRI * HTTP/2.0` preface as a malformed request and typically answers 400 or closes the connection. - 'Cleartext inside the cluster' is a security posture decision, not a free choice. If your threat model includes an attacker on the pod network, h2c between services needs mutual TLS somewhere - usually a service mesh - and then you are back to TLS with ALPN anyway. ## The compact answer h2c is legal but browserless. Without TLS you lose ALPN, so you get either a deprecated Upgrade dance or configured prior knowledge, and cleartext binary traffic trips middleboxes. It survives where both ends are yours and there is no middlebox in between - chiefly gRPC and proxy-to-origin hops.

  • A load balancer terminates TLS with HTTP/2 at the edge. Does the backend automatically get HTTP/2 as well?
    No. The front-end and back-end protocols are configured independently, and most proxies default the upstream leg to HTTP/1.1. That is usually harmless for plain REST but fatal for gRPC, which requires HTTP/2 end to end, and it quietly discards multiplexing and header compression on the internal hop. Verify it explicitly rather than assuming.
  • What does curl's --http2-prior-knowledge mean and when is it safe?
    It tells the client to skip negotiation entirely and send the HTTP/2 connection preface immediately, assuming the server speaks cleartext HTTP/2. It is safe only when you control both ends and know the server's configuration, since an HTTP/1.1-only server sees the preface as a malformed request and responds with an error or closes the connection.

saying these in an interview costs you the question

  • Believing browsers will use HTTP/2 on `http://` URLs
  • Saying HTTP/2 requires TLS by specification - it does not, browsers require it in practice
  • Assuming edge HTTP/2 implies HTTP/2 to the origin
  • Thinking `Upgrade: h2c` is the normal way cleartext HTTP/2 starts today - prior knowledge is, and Upgrade is deprecated
  • Treating cleartext inside a cluster as automatically acceptable without considering the threat model

context