HTTP/1.1

Protocol

HTTP/1.1 is the text-based version of HTTP, first published in January 1997 and defined today by RFC 9112. It adds persistent connections, chunked transfer coding and a mandatory Host header, but carries one response at a time per connection. CDNs still use it on the hop to origin.

9 min read Updated Aug 30, 2026

Full Explanation

HTTP/1.1 is the text-based version of HTTP. RFC 9112 describes it as "a stateless application-level request/response protocol that uses extensible semantics and self-descriptive messages". RFC 9112 is the current specification of its message syntax, framing and connection management. It is not the whole of HTTP. Request semantics live in RFC 9110. Caching lives in RFC 9111. Both are shared by every HTTP version, so HTTP/1.1 is only the wire format. It is also not multiplexed. Unlike HTTP/2 and HTTP/3, it writes messages as readable text, and it delivers one response at a time on a connection. It was first published as RFC 2068 in January 1997. RFC 2616 followed in 1999, then RFC 7230 in 2014. It is the one version every origin is expected to speak. That is why it is still the baseline on the hop from a CDN to your servers.

How it works

A client opens, or reuses, a TCP connection and sends the request as text. RFC 9112 section 2.1 states: "An HTTP/1.1 message consists of a start-line followed by a CRLF and a sequence of octets ... zero or more header field lines ... and an optional message body." A response has the same shape, with a status-line in place of the request-line. Over TLS, the version is picked by ALPN: section 12.4 registers the identifier http/1.1 for it.

Four things separate HTTP/1.1 from HTTP/1.0:

  • Persistent connections. Section 9.3 says HTTP/1.1 "defaults to the use of" persistent connections, "allowing multiple requests and responses to be carried over a single connection". That is what keep-alive names. Persistence is decided from the protocol version and the Connection header field of the most recently received message. A close connection option ends it after the current response.
  • Chunked transfer coding. Section 7.1: "Chunked enables content streams of unknown size to be transferred as a sequence of length-delimited buffers, which enables the sender to retain connection persistence and the recipient to know when it has received the entire message."
  • A mandatory Host header. "A client MUST send a Host header field ... in all HTTP/1.1 request messages" (section 3.2). RFC 9110 section 7.2 gives the purpose: it enables "the origin server to distinguish among resources while servicing requests for multiple host names". That is virtual hosting.
  • Explicitly hop-by-hop control fields. RFC 9110 section 7.6.1: "Intermediaries MUST parse a received Connection header field before a message is forwarded and, for each connection-option in this field, remove any header or trailer field(s) from the message with the same name as the connection-option." Connection, and whatever it lists, stops at each proxy: it does not travel end to end.

HTTP/1.1 has no mechanism for identifying a request. Section 9.2 states: "HTTP/1.1 does not include a request identifier for associating a given request message with its corresponding one or more response messages. Hence, it relies on the order of response arrival to correspond exactly to the order in which requests are made on the same connection." A client may pipeline several requests without waiting for each answer. But the server "MUST send the corresponding responses in the same order that the requests were received" (section 9.3.2). So a connection still yields one response at a time. There is no field compression either. RFC 9113 section 1 notes that "HTTP fields are often repetitive and verbose, causing unnecessary network traffic". That is the cost HTTP/2 field coding was designed to remove.

Why it matters for a CDN

A CDN terminates client connections at an edge server. It fetches whatever it has not cached from the origin. So there are two independent protocol choices: client-to-edge, and edge-to-origin. HTTP/1.1 is the version that is always available on the second one. Fastly states that it "supports any backend that is an HTTP/1.1 compliant web server". Amazon CloudFront documents that "CloudFront forwards requests to your custom origin using HTTP/1.1". AWS's protocol-support article is explicit that "You can't use HTTP/2 between CloudFront and custom origins." Two of its features do real work on that hop. Persistent connections let an edge reuse one origin connection across many client requests, instead of paying a TCP and TLS handshake per fetch. Chunked responses let an origin start streaming before it knows the body length. The edge can then begin forwarding immediately. Its costs are equally concrete. One response at a time per connection means an edge needs a pool of connections to get any concurrency to a single origin. Every header field is also re-sent uncompressed on every request. That is expensive when origin fetches carry large cookie or authorization headers. The protocol version on either hop does not change what is cacheable. Freshness comes from RFC 9111 and the Cache-Control directives. Those directives are the same under HTTP/1.1, HTTP/2 and HTTP/3.

What CDNs do

Behaviour on the origin hop genuinely differs between vendors, so check your own.

  • Cloudflare: HTTP/2 to the origin by default, HTTP/1.1 as the fallback. Cloudflare states: "At Cloudflare, HTTP/2 connection to the origin is enabled by default". It adds: "if the origin does not support HTTP/2, Cloudflare will initiate an HTTP/1.1 connection". The same page states that Cloudflare picks the version from the origin's ALPN advertisement. It gives its reasons for preferring HTTP/2 there: fewer TCP handshakes, lower latency and fewer concurrent connections for the origin to manage.
  • Fastly: HTTP/1.1 is the stated backend requirement, and HTTP/3 is client-side only. Fastly supports "any backend that is an HTTP/1.1 compliant web server". Its HTTP/3 guide adds: "We do not support HTTP/3 between Fastly and your origin servers."
  • CloudFront: HTTP/1.1 to a custom origin. "CloudFront forwards requests to your custom origin using HTTP/1.1". AWS also states that "You can't use HTTP/2 between CloudFront and custom origins". So the version is not a choice on that leg.
  • Reverse proxies in front of an origin. nginx documents that for proxy_http_version: "Since 1.29.7, version 1.1 is used by default. Before 1.29.7, version 1.0 was used by default" (ngx_http_proxy_module). Its keepalive documentation adds that the Connection header field should be cleared for upstream keep-alive to work. On builds older than 1.29.7, an unset proxy_http_version means every upstream fetch is HTTP/1.0, with no persistence.

Watch out for

  • Head-of-line blocking. On a single connection, section 9.4 notes, "a request that takes significant server-side processing and/or transfers very large content would block subsequent requests on the same connection". The RFC records that multiple connections are typically opened precisely to avoid that. One slow object stalls everything queued behind it.
  • Connection churn is the workaround, and it has a price. The spec sets no ceiling. It states: "this specification does not mandate a particular maximum number of connections but, instead, encourages clients to be conservative when opening multiple connections" (section 9.4). The same section warns that "each connection consumes server resources". In practice, browsers settled on roughly six per host. MDN's connection management guide says the default "was once 2 to 3 connections, but this has now increased to a more common use of 6 parallel connections". That is browser convention, not a protocol limit.
  • Pipelining is not a concurrency plan. Responses must stay in order. MDN records that "no modern browser activates this feature by default". RFC 9113 section 1 says pipelining "only partially addressed request concurrency and still suffers from application-layer head-of-line blocking". After a failed connection, a client "MUST NOT pipeline immediately after connection establishment, since the first remaining request in the prior pipeline might have caused an error response that can be lost again" (section 9.3.2).
  • Ambiguous framing is an attack surface. Section 11.2 states: "Request smuggling ... is a technique that exploits differences in protocol parsing among various recipients to hide additional requests (which might otherwise be blocked or disabled by policy) within an apparently harmless request." A message carrying both Transfer-Encoding and Content-Length "ought to be handled as an error". An intermediary that forwards it "MUST first remove the received Content-Length field" (section 6.3). Every hop in a CDN chain is such an intermediary.
  • Chunked request bodies can draw a 411. A client sending a request body "SHOULD use a valid Content-Length header field if the message body length is known in advance, rather than the chunked transfer coding, since some existing services respond to chunked with a 411 (Length Required) status code even though they understand the chunked transfer coding" (section 6.3). Those existing services are typically gateway-fronted origins that need the length before they are called.

Best practice

  • Advertise the newest version your origin supports and let ALPN choose, rather than pinning the hop to HTTP/1.1.
  • Keep the origin hop persistent. On nginx-class proxies, that means proxy_http_version 1.1 (the default only since 1.29.7), and clearing the Connection header field. This way the edge is not re-handshaking per fetch.
  • Send Content-Length on request bodies whose length you know, and reserve chunked for response bodies that are genuinely dynamic.
  • Size a connection pool to the origin for concurrency, or move the hop to HTTP/2. Do not count on pipelining.
  • Make framing unambiguous at every intermediary. Reject or normalise messages that carry both Content-Length and Transfer-Encoding before forwarding them.
  • Trim what travels on every origin fetch. Large cookies and duplicated headers cost bytes on each HTTP/1.1 request, because nothing is compressed.

Examples

# HTTP/1.1 request/response
GET /style.css HTTP/1.1
Host: cdn.example.com
Connection: keep-alive
Accept-Encoding: gzip, br

HTTP/1.1 200 OK
Content-Type: text/css
Content-Encoding: br
Cache-Control: public, max-age=31536000
Connection: keep-alive

# Force HTTP/1.1 with curl
$ curl --http1.1 -I https://cdn.example.com/
HTTP/1.1 200 OK

# Nginx: force HTTP/1.1 to upstream
proxy_http_version 1.1;
proxy_set_header Connection "";

Frequently Asked Questions

HTTP/1.1 is the text-based version of HTTP, first published in January 1997 and defined today by RFC 9112. It adds persistent connections, chunked transfer coding and a mandatory Host header, but carries one response at a time per connection. CDNs still use it on the hop to origin.

# HTTP/1.1 request/response
GET /style.css HTTP/1.1
Host: cdn.example.com
Connection: keep-alive
Accept-Encoding: gzip, br

HTTP/1.1 200 OK
Content-Type: text/css
Content-Encoding: br
Cache-Control: public, max-age=31536000
Connection: keep-alive

# Force HTTP/1.1 with curl
$ curl --http1.1 -I https://cdn.example.com/
HTTP/1.1 200 OK

# Nginx: force HTTP/1.1 to upstream
proxy_http_version 1.1;
proxy_set_header Connection "";

Related CDN concepts include:

  • HTTP/2 — Version of HTTP that carries many concurrent request/response streams over one TCP connection and compresses …
  • HTTP/3 — HTTP/3 is the third major version of HTTP (RFC 9114, June 2022). It runs on …
  • Keep-Alive — Reusing one TCP connection for many HTTP request/response exchanges instead of opening a new connection …
  • TCP (TCP) — TCP is the connection-oriented transport specified in RFC 9293: it delivers application data as one …