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

Steps

  1. 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.0

    An empty result means this build cannot negotiate HTTP/2 and every answer it gives you below is wrong.

  2. 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/
    
    2

    No --http2 flag is needed. Any curl with nghttp2 offers h2 in ALPN on every HTTPS request already.

  3. 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=0

    Same 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.

  4. 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 200

    The 103 is an early hints response. Only HTTP/2 and later can send it, so its presence is a second confirmation.

  5. 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

Sign: curl --http2 answers 1.1 and exits 0, so the server is reported as HTTP/1.1 only.Cause: A curl linked against Schannel has no nghttp2. It accepts --http2, ignores it, never offers h2 in ALPN and returns success. Verified here on 2026-09-11: curl 8.1.2 printed 1.1 for www.cloudflare.com in the same minute that curl 8.21.0 printed 2. Run curl --version and look for nghttp2 before you trust any answer.
Sign: The browser shows h2 but curl shows http/1.1 from the same machine.Cause: The browser resolved a different address, or the CDN edge that answered the browser is not the one that answered curl. Pin the address with --resolve so both requests reach the same node before comparing.
Sign: HTTP/2 works on the public hostname and not on the internal one.Cause: TLS termination on the internal listener has no ALPN configured. HTTP/2 over TLS is only reachable through ALPN, so a listener that never sends the extension can never negotiate it, whatever the application server supports.

What to check next

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.

basic5 minpublished updated Maks Verny