Using HTTP/2 with cURL enables faster and more efficient communication with modern APIs and websites by reducing head-of-line blocking and lowering header overhead. Multiplexed streams, header compression via HPACK, and connection reuse help scripts, health checks, and automation hit endpoints with less latency and better throughput.
When compiled with HTTP/2 support, cURL relies on libcurl and a backend such as nghttp2 to negotiate the protocol during the TLS handshake using ALPN. Command-line switches like --http2 and --write-out expose the negotiated protocol version, while verbose logging reveals the underlying handshake, stream setup, and reuse behavior.
Actual behavior depends on how the cURL binary was built, the capabilities of upstream servers and proxies, and whether cleartext h2c is involved. Forcing HTTP/2 on endpoints that only speak HTTP/1.1 results in connection failures, so version support and negotiation details should be checked before baking HTTP/2 flags into production scripts.
Steps to use HTTP/2 in cURL:
- Display local cURL build information to confirm HTTP/2 support.
$ curl --version curl 8.5.0 (aarch64-unknown-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7 Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6 Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd
Presence of HTTP2 in the Features line confirms that the installed binary can negotiate HTTP/2 connections.
- Send a header-only HTTPS request while explicitly requesting HTTP/2.
$ curl --silent --http2 --head --cacert /work/docker/certs/ca.crt "https://api.example.net/" HTTP/2 200 server: nghttpd nghttp2/1.59.0 cache-control: max-age=3600 date: Sat, 10 Jan 2026 06:29:12 GMT content-length: 63 last-modified: Sat, 10 Jan 2026 06:29:11 GMT content-type: text/html
A status line beginning with HTTP/2 indicates that the server is responding over HTTP/2 instead of HTTP/1.1.
- Inspect verbose output to observe TLS negotiation and protocol selection details.
$ curl --http2 --verbose --cacert /work/docker/certs/ca.crt "https://api.example.net/" --output /dev/null * Host api.example.net:443 was resolved. * IPv6: (none) * IPv4: 127.0.0.1 * Trying 127.0.0.1:443... * Connected to api.example.net (127.0.0.1) port 443 * ALPN: curl offers h2,http/1.1 ##### snipped ##### * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / prime256v1 / RSASSA-PSS * ALPN: server accepted h2 ##### snipped ##### * using HTTP/2 * [HTTP/2] [1] OPENED stream for https://api.example.net/
Lines mentioning ALPN and a message such as using HTTP/2 show that the handshake successfully negotiated HTTP/2.
- Force cleartext HTTP/2 only when targeting a known h2c test endpoint.
$ curl --http2-prior-knowledge --silent --show-error "http://api.example.net/" curl: (56) Remote peer returned unexpected data while we expected SETTINGS frame. Perhaps, peer does not support HTTP/2 properly.
Using --http2-prior-knowledge against servers that only understand HTTP/1.1 causes requests to fail because no protocol downgrade is attempted.
- Print only the negotiated HTTP version as a concise machine-readable indication.
$ curl --http2 --silent --output /dev/null --write-out '%{http_version}\n' --cacert /work/docker/certs/ca.crt "https://api.example.net/" 2
Output of 2 from --write-out '{http_version}' shows successful HTTP/2 use, while 1.1 or 1.0 indicates fallback to an older protocol.
- Test multiplexing behavior by fetching multiple URLs over a single connection.
$ curl --http2 --parallel --silent --write-out '%{url_effective} %{http_code}\n' \ --cacert /work/docker/certs/ca.crt \ -o /dev/null -o /dev/null -o /dev/null \ "https://api.example.net/" \ "https://api.example.net/headers" \ "https://api.example.net/status/200" https://api.example.net/ 200 https://api.example.net/headers 200 https://api.example.net/status/200 200
Concurrent transfers with --parallel over HTTP/2 reuse one connection and can reduce total time for multiple small requests.
- Verify fallback behavior by repeating the version check against an endpoint that only supports HTTP/1.1.
$ curl --http2 --silent --output /dev/null --write-out '%{http_version}\n' "http://downloads.example.net/" 1.1
Different outputs for modern and legacy endpoints confirm that scripts using HTTP/2 flags still behave correctly when servers cannot negotiate HTTP/2.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
