Part 3: Wireshark install and HTTP/2 traffic capture
This article continues the HTTP/2 server push series by describing how to capture and decrypt HTTP/2 traffic with Wireshark. Since HTTP/2 requires TLS (HTTPS) we need to be able to decrypt any captured traffic before it can be inspected. This article describes how to do this with Firefox Developer and Wireshark. I hope you enjoy it.
Check out these previous articles which describe how to configure NGINX web server for HTTP/2 server push in a docker container.
- Install Ubuntu 18 in VirtualBox on Windows 10
- HTTP/2 with NGINX & Wireshark in Ubuntu (Part 1)
- HTTP/2 with NGINX & Wireshark in Ubuntu (Part 2)
Set an environment variable
First, set a new environment variable
SSLKEYLOGFILE with a path to where the SSL logs will be stored.
Open a terminal and enter the below command. I’ve used
$HOMEto reference my home directory (
/home/codeooze/). Ensure to use a valid path for your Ubuntu instance.
You can check the variable is set correctly by using the
exportcommand and checking the value of
SSLKEYLOGFILEis set as expected.
Note that this variable will only exist in your current session. So if you log out or restart Ubuntu, you’ll need to set it again.
Install Firefox Developer
Firefox Developer can be used to capture the SSL key used for encryption between the client (browser) and the web server.
In Ubuntu, open Firefox and navigate to https://www.mozilla.org/en-US/firefox/developer/ and download Firefox Developer.
The version used in this article is
66.0b6 (64-bit)which is the latest at the time of writing.
Extract the archive file.
You can launch Firefox Developer from a terminal by entering the below command. Ensure the path matches the location where you extracted the archive file.
Firefox should automatically create the
sslmaster.txtwhen launched. Open a terminal and change into the location where the
sslmaster.txtfile is expected (e.g.
/home/codeooze/) and verify it has been created. If not, try closing Firefox Developer and re-opening it.
Install and configure Wireshark
Wireshark is a popular network traffic analyzer that we’ll use to capture and inspect HTTP/2 traffic.
Open a new terminal window and enter the following command:
sudo apt-get install wireshark
As part of the install process you will be prompted to decide if non-super users should be allowed to capture packets.
If you are security conscious you should probably select No as recommended by the prompt.
If, however, you are performing these steps in a virtual machine created specifically for this exercise, you can probably select Yes and move on.
I have selected No.
The Wireshark installation will proceed. When finished, launch Wireshark by entering:
sudo wireshark &
Now you need to point Wireshark to the
sslmaster.txtfile so it can use the captured SSL key to decrypt traffic between the browser and NGINX web server.
In Wireshark, from the top menu select Edit > Preferences
In the left pane expand Protocols, then scroll down and select SSL.
Note: There are a lot of protocols listed, but thankfully they are in alphabetic order.
Beside the (Pre)-Master-Secret log filename field click Browse and select the
Click OK to save the changes.
Capture HTTP/2 traffic
Now we’re ready to capture some HTTP/2 traffic.
If the NGINX docker container is not already running, start it now
sudo docker run -dit --name nginx-http -p 80:80 -p 443:443 nginx-http2
In Wireshark, from the top menu select Capture > Start (or alternatively hit Ctrl + E) to start the traffic capture.
Note: If you have multiple network interfaces you may need to ensure the correct one is selected for the capture. I’ve used the docker0 interface which is the network interface for the docker container running NGINX, however it will also work on the loopback interface.
In Firefox Developer navigate to
In Wireshark, stop the capture using Capture > Stop (or alternatively hit Ctrl + E again).
You should see some captured HTTP/2 packets. If not, try clearing the history and cache in Firefox, start the capture again, then refresh the website.
Note: You can filter the captured packets to show HTTP2 packets by typing
http2in the filter field and hitting
The traffic will already be decrypted. To see what the encrypted traffic looks like, select Edit > Preferences > Protocols > SSL and clear (Pre)-Master-Secret log filename. Also clear
http2in the filter.
The encrypted data will show the protocol as TLSv1.2 and the Info column will just show Application Data.
You can confirm these are HTTP/2 packets by comparing the packet numbers. For example, in the above decrypted packet screenshot we have packet numbers
15showing as HTTP/2 when decrypted. These same packet numbers are highlighted in the encrypted capture below and show as Application Data:
Set (Pre)-Master-Secret log filename again so the traffic is decrypted and filter for
Examine the packet with the
GET /home.htmlrequest (packet
14in the screenshots). This is the browser’s
Note: your packet numbers will likely be different, so use the screenshots to help guide you.
15the server has replied with the
home.htmlresource, along with a
PUSH_PROMISEfor each of
Following are several
DATApackets which represent the pushed files. These packets demonstrate HTTP/2 server push and multiplexing.
HTTP/2 packet analysis
In HTTP/2, each request and response is assigned a
stream id. A single packet can then be split into multiple frames, allowing data from more than one resource to be sent in a single packet. This also allows a single resource to be spread across multiple packets. On the client side, all the frames with the same
stream id are collected together to re-form the resource (e.g.
Examine the packet with
15 in the screenshots) by selecting it in the upper pane. (Note: your packet numbers and stream IDs will likely be different, so use the screenshots to help guide you.) In the middle pane, expand HyperText Transfer Protocol 2 and locate this line:
Stream: PUSH_PROMISE, Stream ID: 15, Length 114, GET /bootstrap.css
This is the
/bootstrap.css. It means the web server is telling the client it will push this resource without the client needing to request it separately.
Stream ID: 15 is the current Stream ID of this frame containing the promises. You can also see the stream ID value in the packet list, e.g
PUSH_PROMISE has stream ID 15 (it also happens to be packet 15).
Look down a little further and you will see
Promised-Stream-ID: 2 (highlighted in the above screenshot). This is the future Stream ID that will be used when the server eventually sends frames containing the
/bootstrap.css resource. This tells the client where to look for
/bootstrap.css in future packets. You can easily identify the later packets using Stream ID 2 in the packet list, for example
Keep scrolling through this packet and you will see this repeated for each of the resources the web server has promised to push to the client (
/img2.jpg). They will all have a unique
Scroll down even further and you will find this packet also contains the actual
/home.html resource requested by the client.
Finally, select one of the
DATA packets below the
PUSH_PROMISE[x] packet (packet
15 in the screenshots). I’ve selected packet
20 which is
DATA. In the middle pane, expand HyperText Transfer Protocol 2 and examine it. This packet contains a frame from stream id
2 which we know is
/bootstrap.css. This is corroborated by the line
Header: context-type: text/css. This file is spread across many frames in many packets, all with stream id
Wireshark also tells us that the fully assembled
/bootstrap.css resource can be found in packet
73 later in the capture.
I have provided a very brief analysis of the captured HTTP/2 traffic. I encourage you to explore your packet capture further to help understand HTTP/2. There are many excellent resources detailing how the protocol works, such as the Google Developers Web Fundamentals article Introduction to HTTP/2. Wireshark is a very powerful tool and I encourage you to learn more about it as well.
I hope you have enjoyed this series of articles and have learned something new along the way.