Using HTTP/2 with Apache and Nginx

Published:  22/09/2022 11:00


HTTP is de facto the leading protocol in almost any IT domain.

The advent Cloud Native environments made it even more ubiquitous with their heavy use of REST APIs with every public facing interface using HTTP.

The version 1.1 of the protocol is quite old. It's simple and "traditional" but some of the Internet giants (well, mostly one of them) started to notice that they could greatly benefit from a somewhat updated protocol.

When you own a huge search engine that billions of people load every day, any small gain in bandwidth and computing power can become significant.

Hence the proposal to move forward to HTTP version 2 using a protocol (called SPDY) they were already internally using as the basis.

The new protocol has a feature that would allow a server to tell the client that it can immediately start to fetch some extra static assets (it's called HTTP/2 push and we won't talk about it in this article) which is extremely useful for any large front page or portal that billions of users are loading all the time because these extra requests are immediately pipelined and don't need to be made afterwards.

For you and me though, such a feature is just about useless because it requires custom configuration for every page where you'd want to use it.

The push for HTTP/2 had some criticism and valid concern and took its time to reach a good level of adoption.

Most of the large actors on the web are already using HTTP/3, a more refined protocol addressing the majority of the aforementioned criticism. If you really want to use HTTP/3, we'd recommand using a reverse proxy like HAProxy, while keeping in mind that it's very recent technology and might require frequent updates.

Other projects like .NET 6.0 have been using HTTP/2 by default for some time, though that's still isn't the case for the classic web servers of the Linux world.


All of the procedures below are meant for Debian, version 11 "Bullseye" at the time fo writing.

Other distributions may have different ways of loading Apache or Nginx modules and configuration.

Enabling HTTP/2 on Apache

A few modules have to be enabled:

a2enmod http2 ssl rewrite
service apache2 restart

And that's it, you now have HTTP/2 support for SSL connections.

There are two protocols enabled by the module config we just added:

  • h2 — HTTP/2 over SSL - the only protocol supported by common HTTP clients (web browsers);
  • h2c — HTTP/2 over TCP, without SSL - This protocol is never used. It could make sense for internal proxy and HTTP communication but HTTP clients rarely support it.

Which means you have to use HTTPS to see the HTTP/2 protocol from a web browser, for instance from the network tab of the dev console of your web browser of choice:

Firefox showing protocol version as HTTP/2 in the network inspector

The "deflate" module, responsible for gzip compression, should be automatically enabled for common static assets (plain html, CSS, JavaSript, ...) which allows us to benefit from header compression in the HTTP/2 protocol.

Enabling HTTP/2 on an existing Apache server

Apache can be made to run in very different concurrency models and one of these, called prefork, just doesn't work at all with HTTP/2.

It shouldn't be too much of an issue because prefork is to be avoided anyways (uses more resources and tends to fill up memory quite easily) and hasn't been the default mode for a long time.

However, a lot of "easy to install" PHP setups using the PHP module for Apache will use the prefork model because it's the only one that is thread safe.

If you're using FPM like we usually do, you're fine. Nginx always uses FPM and won't be an issue either.

To see which mode your Apache server is running as, you can try:

apachectl -V | grep -i mpm

It's possible to transition from prefork to FPM, but it might not be easy nor consequence free. What we advice is to use a reverse proxy, which could itself be another Apache, Nginx or HAProxy server.

Enabling HTTP/2 on Nginx

If you installed Nginx through the package nginx-full, HTTP/2 is very easy to enable.

Same remark as with Apache: browsers can only use HTTP/2 through SSL, so we need to enable HTTPS to make it work.

Otherwise it's extremely easy to enable HTTP/2 and get gzip support with Nginx, we just add http2 to the relevant listen directives (those that also have ssl):

listen 443 ssl http2;
listen [::]:443 ssl http2;

You'll find these lines in /etc/nginx/sites-available/default in a fresh install of Nginx on Debian.

Test that the configuration is valid:

nginx -t

Quick remark: you can ignore their comment in the default virtual host config example about disabling gzip as the issue has been fixed for a while now.

Why would I enable HTTP/2?

Honestly, the performance gain will often not be noticable at all.

It can be measured with applications that have a lot of static assets that aren't bundled. I had to create a situation with 350 JavaScript files to see a significant difference of performance in my testing.

Now HTTP/2 has other advantages and may feel more responsive overall with its better flow control. It's certainly not an excuse to avoid bundling common static assets.