How to check if a website uses HTTP/2
Run curl -s -o /dev/null -w '%{http_version}\n' https://www.cloudflare.com/. A curl built with nghttp2 prints 2 when the server negotiated HTTP/2 over ALPN, and 1.1 when it did not. Check the build first with curl --version: a Schannel build has no HTTP/2 and prints 1.1 for every site on the internet.
Checker offline. Follow the manual steps below, they give the same answer.
Why check this
Run this after a change to the load balancer, the CDN or the TLS termination, and again on staging sign-off for any release that claims a page-speed improvement. HTTP/2 carries every request for a host on one connection. When a proxy in front of the origin drops it, a page of sixty assets goes back to queuing, LCP moves by hundreds of milliseconds, and nothing anywhere writes an error line. The protocol column is the only place the regression is visible.
Prerequisites
- A curl build with HTTP/2. Confirm with
curl --version; the library line has to namenghttp2. Background in the curl HTTP/2 documentation. - openssl 3.x for the ALPN read in step 5.
openssl versionis enough to confirm it is there. - The hostname the browser actually requests. An apex and a
wwwname can terminate on different proxies and answer with different protocols.
Steps
- Step 1.
Confirm your own curl can speak the protocol before you point it at anything.
curl --version | grep -o -E 'nghttp2/[0-9.]+|ngtcp2/[0-9.]+|nghttp3/[0-9.]+'nghttp2/1.70.0 ngtcp2/1.25.0 nghttp3/1.18.0An empty result means this build cannot negotiate HTTP/2 and every answer it gives you below is wrong.
- Step 2.
Print the protocol version curl and the server agreed on.
curl -s -o /dev/null -w '%{http_version}\n' https://www.cloudflare.com/2No
--http2flag is needed. Any curl with nghttp2 offersh2in ALPN on every HTTPS request already. - Step 3.
Run the same command on a build without nghttp2 and read what it claims.
curl -s -o /dev/null -w '%{http_version}\n' https://www.cloudflare.com/; echo "exit=$?"1.1 exit=0Same host, same second, opposite answer, and the exit status is zero. This is the result that gets filed as a bug against a server that is fine.
- Step 4.
Read the status line, which names the protocol on every response including interim ones.
curl -sI https://www.cloudflare.com/ | grep -i '^HTTP/'HTTP/2 103 HTTP/2 200The
103is an early hints response. Only HTTP/2 and later can send it, so its presence is a second confirmation. - Step 5.
Ask the TLS layer directly, with no curl involved.
openssl s_client -alpn h2 -connect www.cloudflare.com:443 -servername www.cloudflare.com </dev/null 2>/dev/null | grep -i 'ALPN'ALPN protocol: h2
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| %{http_version} prints 2 | The server negotiated HTTP/2 over ALPN | Nothing. Record the value in the release notes. |
| %{http_version} prints 1.1 and step 1 printed nothing | Your curl has no HTTP/2 | Install a build with nghttp2 and rerun. The server has not been tested yet. |
| %{http_version} prints 1.1 and step 1 printed nghttp2 | The server or a proxy refused h2 in ALPN | Check the proxy in front of the origin, then rerun step 5 against the origin address. |
| ALPN protocol: h2 while curl prints 1.1 | ALPN works but curl was built without it | The build, again. Trust step 5 over step 2. |
| %{http_version} prints 3 | HTTP/3 was used, so HTTP/2 was not exercised | Add --http2 to pin the test to TCP, then read the value again. |
Common mistakes
What to check next
- How to check HTTP/3 support: the next protocol hop, and it needs a different set of libraries again.
- How to check alt-svc header: the header that tells a browser HTTP/3 is available on the same host.
- How to check if keep-alive is enabled: HTTP/2 is useless if the connection is torn down between requests.
- How to check TTFB: the number that moves when a protocol regression reaches production.
- Http2 test: the same negotiation run from the server side, so your local build cannot skew it.
FAQ
How do I know if my website is served over HTTP/1.1 or HTTP/2?
Print %{http_version} with curl, as in step 2. It reports what was negotiated on that request, not what the server can do. A value of 1.1 from a build that has nghttp2 is a real result; from a build without it, it is not.
How do I check HTTP/2 in Chrome DevTools?
Open DevTools, Network tab, right-click any column header and tick Protocol. The column then shows h2, h3 or http/1.1 per request. Reload the page, because the column is filled from the request that was actually made.
How do I check the HTTP version of a website without installing anything?
Use step 5. openssl s_client -alpn h2 reports the negotiated protocol from the TLS handshake alone, and openssl ships with most systems. It tells you whether h2 was accepted, not whether requests then succeeded.
Does a green padlock or a modern TLS version imply HTTP/2?
No. TLS 1.3 and HTTP/2 are separate negotiations. A server can offer TLS 1.3 and still answer only http/1.1 in ALPN, which is exactly what an unconfigured reverse proxy does.
Verified
Verified by Maks Vernycurl 8.21.0curl 8.1.2 Schannelopenssl 3.1.1
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
- Checker: http2-http3 ALPN negotiation for h2, Alt-Svc and direct h3, TLS version
- Web performance checklist
- All performance and delivery checks
basic5 minpublished updated Maks Verny