Skip to content

HTTP/2 Testing Through a Proxy

Paul Reinheimer May 06, 2020 Testing

Something a bit interesting happens when you test an HTTP/2 site through a proxy. Let's look at a request using cURL:

> curl -vvvv -x paul:Puppies22@toronto.wonderproxy.com:12000 https://wonderproxy.com/img/logos/wonderproxy-colour.svg?source=curl
*   Trying 69.42.58.24...
* TCP_NODELAY set
* Connected to (nil) (69.42.58.24) port 12000 (#0)
* Establish HTTP proxy tunnel to wonderproxy.com:443
* Proxy auth using Basic with user 'paul'
> CONNECT wonderproxy.com:443 HTTP/1.1
> Host: wonderproxy.com:443
> Proxy-Authorization: Basic cGF1bDpQdXBwaWVzMjIh
> User-Agent: curl/7.52.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied OK to CONNECT request
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.wonderproxy.com
*  start date: Jan 30 00:00:00 2020 GMT
*  expire date: Feb 19 23:59:59 2022 GMT
*  subjectAltName: host "wonderproxy.com" matched cert's "wonderproxy.com"
*  issuer: C=GB; ST=Greater Manchester; L=Salford; O=Sectigo Limited; CN=Sectigo RSA Domain Validation Secure Server CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55c468e53e90)
> GET /img/logos/wonderproxy-colour.svg?source=curl HTTP/1.1
> Host: wonderproxy.com
> User-Agent: curl/7.52.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< date: Fri, 17 Apr 2020 15:05:24 GMT
< server: Apache
< strict-transport-security: max-age=31536000
< last-modified: Fri, 17 Apr 2020 09:07:41 GMT
< etag: "e5c-5a378e1aa7140"
< accept-ranges: bytes
< content-length: 3676
< vary: Accept-Encoding
< cache-control: max-age=2592000
< expires: Sun, 17 May 2020 15:05:24 GMT
< content-type: image/svg+xml

There's two lines there in particular that are pretty interesting:

< HTTP/1.1 200 Connection established

< HTTP/2 200

Your browser establishes a HTTP/1.1 connection to the proxy server, which then forwards your HTTP/2 connection to the web server. HTTP/2 is always[1] encrypted so the proxy server doesn't see the content of the request,  it's merely instructed to forward the request with the CONNECT header.

Those two lines however can trick Firefox devtools:

Firefox devtools showing an HTTP/1.1 request

In this Firefox window we can see the Switcher installed and activated, and the headers that the browser is reporting HTTP/1.1 for the request. Taking a closer look at the data devtools is giving us:

Expanded headers for the request and response in Firefox devtools

Just looking at the top section Remote Address, Status Code, and Version all list information from the connection to the proxy server, not for the web server. (Chrome developer tools present information correctly.) Referring to the cURL output:

Proxy

* Connected to (nil) (69.42.58.24) port 12000 (#0)
* Establish HTTP proxy tunnel to wonderproxy.com:443
* Proxy auth using Basic with user 'paul'
> CONNECT wonderproxy.com:443 HTTP/1.1
> Host: wonderproxy.com:443
> Proxy-Authorization: Basic cGF1bDpQdXBwaWVzMjIh
> User-Agent: curl/7.52.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established

Web server

* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< date: Fri, 17 Apr 2020 15:05:24 GMT

The browser is reporting the information from the proxy connection, not the information from the actual connection to the web server. That's why we're seeing 200 Connection established, Version: HTTP/1.1, and Remote Address: 69.42.58.24:12000.

Takeaways

  • Testing an HTTP/2 site works great!
  • Developer tools may paint a misleading picture about what's happening (including HTTP version, Server IP, and HTTP response code)
  • The proxy server only sees the domain, and total transferred. Not the content of your requests or responses.

happy testing!

Paul Reinheimer

Developer, support engineer, and occasional manager. I enjoy working on new products, listening to customers, and forgetting to bill them. Also: co-founder.