Setting up HTTP/2 on your web server
This page was originally created on and last edited on .
Introduction to HTTP/2
Hypertext Transfer Protocol (HTTP) is the way your web browser talks to a web server to ask for web pages and the resources they need. At it's heart it's a very simple protocol and has not moved on much in the last 20 years since HTTP/1.1 came on the scene in 1996 (HTTP/1.0 was very basic and almost immediately superseded by 1.1 as the world wide web was becoming mainstream so we'll ignore it). For various reasons it's showing it's age and is causing a performance bottleneck in today's world of rich web pages and applications. This has led to several work arounds which have their own drawbacks. HTTP/2 is the next version of the protocol which aims to address a lot of the issues we have seen. And apparently it is written HTTP/2 and not HTTP 2 or HTTP/2.0
Why do we need HTTP/2?
The main issue with HTTP/1.1 is that it is, basically synchronous. This means you request a resource from a web server (be it a web page, an image, a stylesheet...etc.) and you have to wait for a response. Typically a web page has many resources on it, so after you have downloaded the original webpage, you have a queue of resources to request from the web server. Most web resources are not actually that large, so a lot of that waiting time will be simply waiting for the request to travel across the internet and back. As websites and web applications get richer and richer this gets more and more of a problem. The average page size now has 100 requests in it. Now it still only takes milliseconds to travel have and forth but those start to add up when you have 100 resources to download. Imagine wanting to order several books from Amazon and having to wait for each to arrive, before you could order the next book - a crazy way of doing it, but that's basically what HTTP/1.1 does. It would be much better to fire off all requests to Amazon and it can ship them out to you as fast as it can (ignoring postage costs) and HTTP/2 works like that. There are several workarounds though that are currently used for HTTP/1.1:
- Multiple Connections: Browsers will make several connections to a web server (typically a max of 6-8). The downside is it that these connections require time to set up, resources to manage and also can flood the web server. It's a hack to get around the limitations of HTTP/1.1
- Sharding: Websites will often put other resources on other domains (for example static.example.com), to allow another set of 6-8 connections to be made. As it's another domain this requires even more hassle for the browser to set up, and unless you have more than the 6-8 max of every request this can actually slow down your browsing
- Spriting: Similar to concatenating resources you can put multiple images together in one big image, and download it once, and then refer to each image by coordinates to pull out the smaller images. This similarly has issues with downloading large files when you might only need some of them, and again caching is lost if you need to add an image to a sprite file.
All of these are hacks because of a fundamental issue with HTTP/1.1. It would be much better to fire off all the requests, and then deal with them as they come in. HTTP/2 allows this and therefore drastically reduces the need for the hacks and the downsides I have listed above.
HTTP/2 also has some other benefits including: It's a binary rather than text based protocol, it allows compression of HTTP Headers, it allows the server to push requests (e.g. if you ask for the home page, it might push the stylesheet and other resources it knows you will need). This last one will be interesting but is not widely supported yet. The main benefit at present is the ability to send and receive a number of requests at once. I've a separate post about HTTP/2 Server Push.
How to set up HTTP/2 on Apache from source
HTTP/2 requires HTTPS so if you don't run that on your server, there is little point in continuing until you get that set up. Well to be honest, technically the spec doesn't require HTTPS but all browser vendors have said they will only support this over HTTPS so it might as well. Additionally some browsers also requires a decent HTTPS set up with modern ciphers. The HTTP/2 spec blacklists a number of older ciphers and so some browsers (e.g. Chrome) will give an error if you try to use HTTP/2 when your server is configured to use these ciphers. HTTP/2 also requires a fairly recent version of openssl (1.0.2 or above) - which also means you've now excuse not to use a decent cipher list :-)
Apache is usually installed as part of package manager on Linux (e.g. yum or apt-get) but these often lag behind the latest versions, so, while you might be lucky enough to be on a linux install with the latest Apache version (Debian seems very up to date for example), it's likely you will need to download and compile the source code to get the latest version. This sounds more intimidating that it really is however I will advise that should only be attempted by experienced system administrators. Package Managers allow you to easily keep on top of security patches whereas installing from source does not. Additionally source packages should be verified to ensure they have not been tampered with when downloading them, which is not covered in these steps (see the Apache documentation on how to verify files). With that out of the way, the below commands will download and install the latest Apache into the default /usr/local/apache2 directory and enable HTTP/2 support on Centos or Red Hat Linux:
#You may need to install some dependencies if not already installed on your machine. #For Centos/RHEL 7 the following should do this for you: sudo yum install wget sudo yum install perl sudo yum install zlib-devel sudo yum install gcc sudo yum install gcc-c++ sudo yum install pcre-devel sudo yum install libxml2-devel sudo yum install openssl-devel sudo yum install expat-devel sudo yum install cmake sudo yum install git sudo yum install automake sudo yum install autoconf sudo yum install libtool cd ~ mkdir sources cd sources #Download and install latest version of openssl #Note mod_http2 requires AT LEAST openssl 1.0.2. At this is often not available on linux builds so you need to install from source #Note also that only Apache 2.4.36 or up will work with openssl 1.1.1 (use openssl 1.1.0 if not on this version yet - note need Apache 2.4.26 for this). #Get it from http://openssl.org/source/ #For example: #wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz #wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz.asc #Verify the package after download (not covered here but can be done with: gpg --recv-keys KEYID followed up by gpg --verify openssl-1.1.1g.tar.gz.asc) tar -zxvf openssl-1.1.1g.tar.gz cd openssl-1.1.1g ./config shared zlib-dynamic --prefix=/usr/local/ssl make sudo make install cd .. #Download and install nghttp2 (needed for mod_http2). #Get it from https://nghttp2.org/ #Latest version here: https://github.com/nghttp2/nghttp2/releases/ #For example: #wget https://github.com/nghttp2/nghttp2/releases/download/v1.34.0/nghttp2-1.34.0.tar.gz #Verify the package after download (not covered here) tar -zxvf nghttp2-1.34.0.tar.gz cd nghttp2-1.34.0 ./configure make sudo make install #Note if you see an error about Python Path then try the following to set the PYTHONPATH variable first #sudo su - #cd to the directory containing nghttp2 source #export PYTHONPATH=/usr/local/lib64/python2.7/site-packages/ #make install cd .. #Download and install latest apr #Note if using openssl 1.1.0 then need to be on APR 1.6 or above #Get it from http://apr.apache.org/ #For example: #wget http://mirrors.whoishostingthis.com/apache/apr/apr-1.7.0.tar.gz #wget https://www.apache.org/dist/apr/apr-1.7.0.tar.gz.asc #Verify the package after download (not covered here but can be done with: gpg --verify apr-1.7.0.tar.gz.asc) tar -zxvf apr-1.7.0.tar.gz cd apr-1.7.0 ./configure make sudo make install cd .. #Download and install latest apr-util #Note if using openssl 1.1.0 then need to be on APR-UTIL 1.6 or above #Get it from http://apr.apache.org/ #For example: #wget http://mirrors.whoishostingthis.com/apache/apr/apr-util-1.6.1.tar.gz #wget https://www.apache.org/dist/apr/apr-util-1.6.1.tar.gz.asc #Verify the package after download (not covered here but can be done with: gpg --verify apr-util-1.6.1.tar.gz.asc) tar -zxvf apr-util-1.6.1.tar.gz cd apr-util-1.6.1 ./configure --with-apr=/usr/local/apr make sudo make install cd .. #Download and install brotli #Not necessary for HTTP/2 but if you care about performance then might as well include it. #And my configure command below assumes you want brotli #If you skip this step then make sure you remove "--enable-brotli --with-brotli=/usr/local/brotli" from configure command when building httpd. #For more information see here: https://www.tunetheweb.com/performance/brotli/ git clone https://github.com/google/brotli.git cd brotli/ git checkout v1.0 mkdir out && cd out ../configure-cmake make make test sudo make install cd ../.. #Download and install apache #Get it from: http://httpd.apache.org/download.cgi#apache24 #For example: #wget http://mirrors.whoishostingthis.com/apache/httpd/httpd-2.4.46.tar.gz #wget https://www.apache.org/dist/httpd/httpd-2.4.46.tar.gz.asc #Verify the package after download (not covered here but can be done with: gpg --verify httpd-2.4.46.tar.gz.asc) tar -zxvf httpd-2.4.46.tar.gz cd httpd-2.4.46 cp -r ../apr-1.7.0 srclib/apr cp -r ../apr-util-1.6.1 srclib/apr-util ./configure --with-ssl=/usr/local/ssl --with-pcre=/usr/bin/pcre-config --enable-unique-id --enable-ssl --enable-so --with-included-apr --enable-http2 --enable-brotli --with-brotli=/usr/local/brotli make sudo make install cd ..
After this you need to add the /usr/local/lib/ and /usr/local/ssl/lib directories to the LD_LIBRARY_PATH. This can be set by either setting the LD_LIBRARY_PATH variable for the user, or by editing the /usr/local/apache2/bin/envvars script:
if test "x$LD_LIBRARY_PATH" != "x" ; then LD_LIBRARY_PATH="/usr/local/apache2/lib:/usr/local/lib/:/usr/local/ssl/lib:$LD_LIBRARY_PATH" else LD_LIBRARY_PATH="/usr/local/apache2/lib:/usr/local/lib:/usr/local/ssl/lib" fi
Next up you need to enable HTTP/2. This involved adding the following line to your apache config:
LoadModule http2_module modules/mod_http2.so
You also might want to add some debug for this module:
<IfModule http2_module> LogLevel http2:info </IfModule>
And finally, turn on the HTTP/2 protocol:
#Enable HTTP/2 support Protocols h2 http/1.1
After this stop and restart your apache (not just a graceful restart) using the apachectl script rather than just the httpd command so that the above env variables are picked up:
/usr/local/apache2/bin/apachectl -k stop
Note if you are running apache from a previous installation which was not in /usr/local/apache2 then you will need to use that stop command to stop it. Keep checking "ps" to ensure https is fully stopped running:
ps -ef | grep httpd | grep -v grep
Once it's fully down start it up again, you can start it up again:
/usr/local/apache2/bin/apachectl -k start
If all has gone well then you should see something like the following in your main error log file:
[Thu Sep 19 09:12:07.003271 2019] [http2:info] [pid 12345:tid 123456789012345] mod_http2 (v1.15.4, feats=CHPRIO+SHA256+INVHD+DWINS, nghttp2 1.34.0), initializing... [Thu Sep 19 09:12:07.004910 2019] [mpm_event:notice] [pid 12345:tid 123456789012345] AH00489: Apache/2.4.46 (Unix) OpenSSL/1.1.1g configured -- resuming normal operations
If that works then you're looking good and can test a request from Chrome, by opening developer tools, adding protocol and making a request. It should hopefully look like this:
While Chrome Developer Tools does show HTTP/2 status, I have heard that it doesn't truly measure the performance boast given to HTTP/2 properly in the network tab. So better to measure the whole time, or perhaps use a tool like webpagetest.org to see any end to end improvement.
Note that if you want the latest and greatest Apache HTTP/2 support then check out the GitHub home page of the module's author (Stefan Eissing) at mod_h[ttp]2 and nghttp2's owner (Tatsuhiro Tsujikawa) for the underlying nghttp2 library it uses). So you may want to install the latest version, even before the next version of Apache comes out. For example since Apache 2.4.46 (which includes mod_http2 1.15.14) there has other releases with bug fixes. To install the latest mod_http2 from source follow these instructions:
#Download and install mod_http2 outside of a regular Apache release #Latest version is here: https://github.com/icing/mod_h2/releases/ wget https://github.com/icing/mod_h2/releases/download/v1.15.16/mod_http2-1.15.16.tar.gz tar -zxvf mod_http2-1.15.16.tar.gz cd mod_http2-1.15.16 #Reconfigure to avoid error messages about func_munge_path_list autoreconf -fi ./configure --with-apxs=/usr/local/apache2/bin/apxs make sudo make install
Browser support is actually very strong, and the HTTP/2 is an upgrade request of HTTP/1.1 so those browsers that do not support it will continue to use HTTP/1.1 without issue.
Web server support is a bit behind the browsers. Apache added support for HTTP/2 from 2.4.17 though until 2.4.26 this was marked as experimental. Similarly Nginx released HTTP/2 support from 1.9.5, though again it was initially marked as experimental (Scott Helme has a similar blog entry to this one on how to install HTTP/2 on Nginx). Despite the experimental tag, these were fairly stable from the feedback online, and now they have both dropped this tag, we should see adoption increasing.
NodeJS had a problem with the upgrade header that some HTTP/2 servers send (including Apache from 2.4.18 onwards), which could cause some problems). I noticed this after upgrading to HTTP/2 and UptimeRobot stopped working for example as they use NodeJS (Update 9-April-2016: Fixed now!). As Node is fairly popular you may notice other issues like this for those running older versions (prior to version 4).
The main downside at present is the lack of support in production stable versions on the web server side. Although Apache and Nginx no longer consider these experimental they are still not in the stable builds that most operating systems supply by default using their package managers (e.g. yum or apt-get), so require extra steps to install. So far I've been running it on this blog since it was launched for Apache in 2015 with almost no issues - the one Node issue noted above is the main thing for now and that's a problem with Node rather than Apache. Saying that using a third party CDN like Cloudflare may be an option and in fact they may already have upgraded you without your noticing!
As mentioned above HTTP/2 also requires HTTPS to work in the browsers, and more than that, some browsers also require good HTTPS config to use HTTP/2 - but really why would you not want to ensure you have good HTTPS config if you're proficient enough to get HTTP/2 up and running!. Run your site through SSLLabs HTTPS Testing tool before you upgrade to HTTP/2 and see also my tips on securing your HTTPS set-up.
And finally support in other tools for HTTP/2 is still being implemented. Cloudflare have an excellent list of resources for testing HTTP/2, but some of your regular tools (e.g. Chrome Developer Tools), may not be reporting accurate statistics at present.
HTTP/2 is an exciting new version of the main way our browsers talk to web servers. There are huge performance gains here. This website is very simple, with few resources, but even that has seen a speed improvement of 15%. This will be larger for more complex sites, and also over lower latency networks (e.g. browsing on mobile phones). At the moment I would not quite recommend installing on your production servers, but it's not far off and soon will be ready for the mainstream. Many big tech companies (e.g Google) are already on HTTP/2 and others (e.g. Facebook) are on the older SPDY protocol so will surely not be far behind. As I keep stating, performance is key in this world and HTTP/2 gives us a good jump up on that.
Some of the CDNs are offering HTTP/2 as part of their package (e.g. Cloudflare have excellent HTTP/2 support and are far and away the biggest implementers of HTTP/2 at the time of writing). If you really want HTTP/2 on your website you could do a lot worse than looking at them`for a hassle free set up.
I've written a follow up blog on HTTP/2 Server Push, which I've started using on this site as an experiment.
Interested in learning more?
I'm so excited about HTTP/2 that I've written a book about it! HTTP/2 in Action published by Manning, and you can read the first chapter for free !
Want to read more?
More Resources on HTTP/2
- My Book "HTTP/2 in Action" is a complete guide to HTTP/2.
- Why do we need HTTP/2? - an excerpt from "HTTP/2 in Action".
- HTTP/2 official website.
- HTTP/2 RFC.
- HTTP/2 Server Push.
- Ilya Grigorik's presentation on how yesterday's performance workarounds have become anti-patterns in HTTP/2.
- The State and Rate of Adoption by Daniel Stenberg.
- Free eBook from Daniel Stenberg detailing HTTP/2.
- Stefan Eissing (the author of mod_http2) has a page on his work on this so far.
- Git Hub page of mod_http2.
- Scott's blog on how to install HTTP/2 on Nginx.
- Excellent list of resources for testing HTTP/2.
This page was originally created on and last edited on .Tweet