How to Install HTTP/3 quic on Nginx Server for Ubuntu

Currently, there are three protocol used in today’s web browser, the first one is the http/1, followed with http/2 and the upcoming protocol, the http/3. The latter is quite promising as it improves the http/2 more because it uses the UDP instead of TCP to load your webpages and assets. It also fixes some problems on previous protocols.

Looking for resources on installing http/3 in the internet is either outdated or end up with errors when installing additional module, especially to those who are not familiar with repo compilation.

Table of Content

  1. NGINX Source Code
  2. Installing NGINX quic
  3. Installing BoringSSL
  4. Adding BoringSSL and HTTP/3 to NGINX Source
  5. Installing other modules
    1. Pagespeed
    2. Brotli
    3. Adding Pagespeed and Brotli to NGINX
  6. Compiling NGINX quic
  7. Setting up NGINX quic

The only problem with implementing the current status of http/3 into your server is, there are still many bugs like random 404 errors on some content of your website (jpeg, fonts, script files).

NGINX Source Code

Looking for source code compiler that allows you to create a deb package for the NGINX quic is hard and you need to manually create it. In this guide, we will teach you how to create a deb package installer for your NGINX quic using the NGINX source code which has similar file structure.

So first, login to your SSH server and make sure you’re at the User’s directory.

cd ~/

Make sure your server is up to date with the latest patches.

sudo apt-get update
sudo apt-get upgrade

Now, we have to install the dev tool for us to compile the NGINX source.

sudo apt-get install dpkg-dev
sudo apt-get install uuid-dev

We need to create a key signature so we can download repo from NGINX packages.

sudo wget https://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

After that, we have to edit the /etc/apt/sources/list and add the NGINX repositories.

vim /etc/apt/sources.list

At the bottom, add the following lines. The example below is for Ubuntu 18.04.5 LTS.

deb https://nginx.org/packages/mainline/ubuntu bionic nginx
deb-src https://nginx.org/packages/mainline/ubuntu bionic nginx

If you’re using other version of Ubuntu, just replace bionic with the following:

  • Ubuntu 20.10: groovy
  • Ubuntu 20.04.1 LTS: focal
  • Ubuntu 18.04.5 LTS: bionic
  • Ubuntu 16.04.7 LTS: xenial
  • Ubuntu 14.04.6 LTS: trusty
  • Ubuntu 12.04.5 LTS: precise

In case you get an error that says “Skipping acquire of configured file ‘nginx/binary-i386/Packages’ as repository ‘https://nginx.org/packages/mainline/ubuntu bionic InRelease’ doesn’t support architecture ‘i386”, you just need to add [arch-amd64] to the deb line.

deb [arch=amd64] https://nginx.org/packages/mainline/ubuntu bionic nginx
deb-src https://nginx.org/packages/mainline/ubuntu bionic nginx

Next is we have to update so that we can get the NGINX repo.

sudo apt-get update
sudo apt-get upgrade

We can now then build dependencies for NGINX and pull the source code to our user’s directory.

sudo apt-get build-dep nginx
sudo apt-get source nginx

After executing code above, it will create a folder named nginx_XXXXX. XXXXX is the version number of Ubuntu.

Installing NGINX quic

Since we have now the NGINX source code, what we need to do now is to replace the code with NGINX quic. But before that, we have to install mercurial as this is needed to compile the NGINX.

sudo apt-get install mercurial

Then we will clone the latest NGINX repo from https://hg.nginx.org/nginx-quic.

cd ~/
hg clone -b quic https://hg.nginx.org/nginx-quic

Let’s overwrite all content of our nginx-quic directory to the nginx source code folder.

rsync -r nginx-quic/ nginx-1.19.6

Installing BoringSSL

Last thing we need to do is to add the BoringSSL as our module for our SSL, it is a forked from OpenSSL from Google that add a quic support. Currently, it is only the available OpenSSL that we can use, if we find one in the future, we will also add them on this guide.

But first, we have to install these dependencies to compile BoringSSL.

sudo apt-get install golang
sudo apt-get install libunwind-dev

We can now pull a copy of boringssl from the official repo Github page.

cd ~/
git clone https://github.com/google/boringssl

Then, let’s compile BoringSSL with cmake and make.

cd boringssl/
mkdir build
cd build
cmake ../
make -j 8

Adding BoringSSL and HTTP/3 to NGINX

After that, we have to add important configuration for our NGINX quic, we can add inside the rules file at NGINX source code folder.

vim  ~/nginx-1.19.6/debian/rules

Then add the following lines that says config.env.nginx and config.env.nginx_debug, you can find it at line 41 and line 46. Then add the following after –with-stream_ssl_preread_module:

--with-http_v3_module --with-http_quic_module --with-stream_quic_module 

Then we need to add -Wno-ignored-qualifiers at the CFLAGS = “” to disable compiler from throwing qualifiers error. It should be:

CFLAGS="-Wno-ignored-qualifiers"

Next we have to add the boringssl module to –with-cc-opt and –with-ld-opt with the following values. You need to replace the existing option at the bottom of the config, or else you’ll get an saying, “./auto/configure: error: certain modules require OpenSSL QUIC support. You can either do not enable the modules, or install the OpenSSL library into the system, or build the OpenSSL library statically from the source with nginx by using –with-openssl= option.”. See the final rules for reference.

--with-cc-opt="-I../modules/boringssl/include $(CFLAGS)" --with-ld-opt="-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS)"

The final rules should look like below:

config.status.nginx: config.env.nginx
    cd $(BUILDDIR_nginx) && \
    CFLAGS="-Wno-ignored-qualifiers" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_v3_module --with-http_quic_module --with-stream_quic_module --with-cc-opt="-I../modules/boringssl/include $(CFLAGS)" --with-ld-opt="-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS)"

Make sure you do the same changes on both line config.env.nginx and config.env.nginx_debug.

Installing other modules

If you need to add important optimization module for your http/3 server like Pagespeed and Brotli, you can also follow the quick guide below. If you need detailed guide for this, you can check our separate post.

This step requires unzip, if you haven’t have this yet on your server, you can install it with this command:

sudo apt-get install unzip

Once done adding the unzip on your server, you can now proceed on installing Pagespeed and Brotli

Pagespeed

For adding pagespeed, we have to get the official Github repository and unzip it on the debian/modules directory.

mkdir ~/nginx-1.19.6/debian/modules
cd ~/nginx-1.19.6/debian/modules
wget https://github.com/apache/incubator-pagespeed-ngx/archive/v1.13.35.2-stable.zip
unzip v1.13.35.2-stable.zip
sudo mv incubator-pagespeed-ngx-1.13.35.2-stable ngx_pagespeed

Then inside ngx_pagespeed folder, we have to add the libraries for pagespeed, the PSOL.

cd ngx_pagespeed
sudo wget https://dl.google.com/dl/page-speed/psol/1.13.35.2-x64.tar.gz
sudo tar -xzvf 1.13.35.2-x64.tar.gz

Our repository for Pagespeed is done.

Brotli

For Brotli, we have just to download the repository from Github to the debian/modules folder.

cd ~/nginx-1.19.6/debian/modules
git clone --recursive https://github.com/google/ngx_brotli

Brotli file is now added, the last thing we have to do is to include it on the NGINX configuration.

Adding PageSpeed and Brotli to NGINX

To add PageSpeed and Brotli to NGINX, similar to adding BoringSSL and http/3 on our NGINX, we have to add it on the debian/rules compile configuration.

vim  ~/nginx-1.19.6/debian/rules

At the ./configure of config.env.nginx and config.env.nginx_debug, add the following lines right after --sbin-path=/usr/sbin/nginx:

--add-module="$(CURDIR)/debian/modules/ngx_pagespeed" --add-module="$(CURDIR)/debian/modules/ngx_brotli" 

Your final configuration rules should look like below.

config.status.nginx: config.env.nginx
    cd $(BUILDDIR_nginx) && \
    CFLAGS="-Wno-ignored-qualifiers" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --add-module="$(CURDIR)/debian/modules/ngx_pagespeed" --add-module="$(CURDIR)/debian/modules/ngx_brotli" --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_v3_module --with-http_quic_module --with-stream_quic_module --with-cc-opt="-I../modules/boringssl/include $(CFLAGS)" --with-ld-opt="-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS)"

Everything is now set for other module, we can now proceed on compiling NGINX.

Compiling NGINX quic

Before we compile, we have to add some finishing touch so we distinguish we are using mod build for NGINX.

First, we have to edit the changelog file.

vim ~/nginx-1.19.6/debian/changelog

Then add +pagespeed+brotli+http3+quic on the first line.

nginx (1.19.6-1~bionic+pagespeed+brotli+http3+quic) bionic; urgency=low

  * 1.19.6-1

 -- YOUR_NAME <your_name@domain.com>  Tue, 24 Nov 2020 16:02:03 +0300

After that, we can now compile our NGINX.

cd ~/nginx-1.19.6/
sudo dpkg-buildpackage -b

Once it started compiling, it will asked if you want to include PSOL debug version, just type yes. After it completes, it will create the deb file at the parent directory. You can then use those to install nginx.

sudo dpkg -i nginx_1.19.6-1~bionic+pagespeed+brotli_amd64.deb

From here we can now check if our new NGINX quic is now running.

nginx -t

Setting up Nginx quic

The last thing we need to do in order for our HTTP/3, quic and other modules to run smoothly on our server is to make few changes to its configuration files and file system needed.

First, we have to fixed the default configuration of NGINX and apply the server block directory. We to remove the default configuration and have to edit the nginx.conf.

rm /etc/nginx/conf.d/default.conf
vim /etc/nginx/nginx.conf

Then replace all content with the following:

user www-data;
worker_processes auto;
include /etc/nginx/modules-enabled/*.conf;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;


    ##
    # PageSpeed Settings
    ##

    pagespeed on;
    pagespeed FileCachePath /var/ngx_pagespeed_cache;
    
    ##
    # Access/Error Log Settings
    ##

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    error_log /var/log/nginx/error.log;

    ##
    # Http Core Module Settings
    ##

    sendfile        on;
    tcp_nopush		on;
    tcp_nodelay 	on;
    keepalive_timeout  65;
    types_hash_max_size 2048;

    ##
    # Gzip Settings
    ##

    pagespeed FetchWithGzip off;
    pagespeed HttpCacheCompressionLevel 0;
    #gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/x-font-ttf application/x-web-app-manifest+json application/xml+rss text/javascript image/svg+xml image/x-icon;

    ##
    # Brotli Settings
    ##

    brotli on;
    brotli_comp_level 6;
    brotli_static on;
    brotli_types application/octec-stream text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript application/x-javascript text/plain application/x-font-trutype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap application/x-web-app-manifest+json;
    
    ##
    # SSL Configuration
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;


    ##
    # FastCGI Cache Settings
    ##

    fastcgi_cache_path /etc/nginx-cache levels=1:2 keys_zone=phpcache:100m inactive=60m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_ignore_headers Cache-Control Expires;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

    
}

Then let’s setup our www directory.

mkdir /var/www
sudo chown -R www-data:www-data /var/www

Now we can now make the server block directory.

mkdir /etc/nginx/sites-available
ln -s /etc/nginx/sites-available /etc/nginx/sites-enabled

Then let’s create a configuration for our website.

vim /etc/nginx/sites-available/yourdomain.com

For now, let’s just add basic configuration.

server{
    listen 80;
    index index.html index.nginx-debian.html;
    server_name yourdomain.com www.yourdomain.com

    root /var/www/yourdomain.com;
}

Then make sure to add SSL on your website, by running certbot. If you don’t how certbot, you can do the following:

sudo apt-get update
sudo apt-get install python3-certbot-nginx

If you’re using Ubuntu below 20.04.2 LTS, just add the repository below and repeat above command:

sudo add-apt-repository ppa:certbot/certbot

Then, create an SSL for your website.

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

After that, certbot will edit your configuration file and add the SSL, we can now edit the file and add the following:

server{
    listen 443 http3 quic reuseport;
    listen 443 ssl http2;

    quic_retry on;
    ssl_early_data on;

    http3_max_field_size 5000;
    http3_max_table_capacity 50;
    http3_max_blocked_streams 30;
    http3_max_concurrent_pushes 30;
    http3_push 10;
    http3_push_preload on;

    add_header alt-svc '$quic=":443"; ma=3600';

    index index.html index.nginx-debian.html;
    server_name yourdomain.com www.yourdomain.com

    root /var/www/yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 

}
server{
    if ($host = http3.codefaq.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;

    server_name yourdomain.com;
    return 404; # managed by Certbot


}

The add_header alt-svc is need to make sure that web browser will know that your server supported http/3. Other settings also needs to setup in order for the NGINX quic not to produce 404 error on your file assets, which are the following:

  • http3_max_field_size 5000;
  • http3_max_table_capacity 50;
  • http3_max_blocked_streams 30;
  • http3_max_concurrent_pushes 30;
  • http3_push 10;
  • http3_push_preload on;

I’ll try to add some explanation on this later on and check the source code as for now, there are no explanation about these on the documentation. But after implementing above configuration, the 404 error on the website was fixed.

After the changes don’t forget to restart the nginx.

sudo service nginx restart

Check your website and you should now see h3-29 on the protocol header when checking it on Web browser’s Developer tools. You can also check your website at the following http/3 checker tools:

OCSP is disabled on BoringSSL which uses quic

If you are implementing OCSP on your NGINX server, unfortunately, the OCSP was removed from BoringSSL and the only way to bring it back is using a patch (https://github.com/kn007/patch/issues/4) from a third party developer. But it only supports SSL stapling file which needs an additional task like Cron job to produce this file.

You’ll get the following warning when you have ssl_stapling on your nginx conf.

nginx: [warn] "ssl_stapling" ignored, not supported

If you’re receiving the following errors, just follow the step above.

./auto/configure: error: certain modules require OpenSSL QUIC support. You can either do not enable the modules, or install the OpenSSL library into the system, or build the OpenSSL library statically from the source with nginx by using --with-openssl=<path> option.

9 Comments

  • Lemonol
    Posted February 19, 2021 6:22 am 0Likes

    I am getting the error “./auto/configure: error: certain modules require OpenSSL QUIC support. You can either do not enable the modules, or install the OpenSSL library into the system, or build the OpenSSL library statically from the source with nginx by using –with-openssl= option.”

    But I’m not sure which “step” to follow above. You don’t clarify. I’m stuck now thanks.

    • CodeFAQ
      Posted February 21, 2021 12:40 am 0Likes

      Hi Lemonol, Make sure to follow this step (Adding BoringSSL and http/3 to nginx): https://codefaq.org/server/how-to-install-http-3-quic-on-nginx-server-for-ubuntu/#adding-boringssl-and-http3

      • Benny L.E.P
        Posted April 23, 2021 1:08 pm 0Likes

        Having problem like Lemonol but using nginx 1.19.10 and follow your suggestion, its still error

        CFLAGS=”-Wno-ignored-qualifiers” ./configure –prefix=/etc/nginx –sbin-path=/usr/sbin/nginx –add-module=”$(CURDIR)/debian/modules/ngx_pagespeed” –add-module=”$(CURDIR)/debian/modules/ngx_brotli” –modules-path=/usr/lib/nginx/modules –conf-path=/etc/nginx/nginx.conf –error-log-path=/var/log/nginx/error.log –http-log-path=/var/log/nginx/access.log –pid-path=/var/run/nginx.pid –lock-path=/var/run/nginx.lock –http-client-body-temp-path=/var/cache/nginx/client_temp –http-proxy-temp-path=/var/cache/nginx/proxy_temp –http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp –http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp –http-scgi-temp-path=/var/cache/nginx/scgi_temp –user=nginx –group=nginx –with-compat –with-file-aio –with-threads –with-http_addition_module –with-http_auth_request_module –with-http_dav_module –with-http_flv_module –with-http_gunzip_module –with-http_gzip_static_module –with-http_mp4_module –with-http_random_index_module –with-http_realip_module –with-http_secure_link_module –with-http_slice_module –with-http_ssl_module –with-http_stub_status_module –with-http_sub_module –with-http_v2_module –with-mail –with-mail_ssl_module –with-stream –with-stream_realip_module –with-stream_ssl_module –with-stream_ssl_preread_module –with-http_v3_module –with-http_quic_module –with-stream_quic_module –with-cc-opt=”-I../modules/boringssl/include $(CFLAGS)” –with-ld-opt=”-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS)”

        • CodeFAQ
          Posted April 23, 2021 1:11 pm 0Likes

          Hi Benny, is these option –with-cc-opt=”-I../modules/boringssl/include $(CFLAGS)” –with-ld-opt=”-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS) at the last part of the values?

  • CodeFAQ
    Posted April 23, 2021 1:10 pm 0Likes

    Hi Benny, is these option –with-cc-opt=”-I../modules/boringssl/include $(CFLAGS)” –with-ld-opt=”-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS) at the last part of the values?

    • Benny L.E.P
      Posted April 23, 2021 1:57 pm 0Likes

      Yes, this is my CFLAGS

      CFLAGS=”-Wno-ignored-qualifiers” ./configure –prefix=/etc/nginx –sbin-path=/usr/sbin/nginx –add-module=”$(CURDIR)/debian/modules/ngx_pagespeed” –add-module=”$(CURDIR)/debian/modules/ngx_brotli” –modules-path=/usr/lib/nginx/modules –conf-path=/etc/nginx/nginx.conf –error-log-path=/var/log/nginx/error.log –http-log-path=/var/log/nginx/access.log –pid-path=/var/run/nginx.pid –lock-path=/var/run/nginx.lock –http-client-body-temp-path=/var/cache/nginx/client_temp –http-proxy-temp-path=/var/cache/nginx/proxy_temp –http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp –http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp –http-scgi-temp-path=/var/cache/nginx/scgi_temp –user=nginx –group=nginx –with-compat –with-file-aio –with-threads –with-http_addition_module –with-http_auth_request_module –with-http_dav_module –with-http_flv_module –with-http_gunzip_module –with-http_gzip_static_module –with-http_mp4_module –with-http_random_index_module –with-http_realip_module –with-http_secure_link_module –with-http_slice_module –with-http_ssl_module –with-http_stub_status_module –with-http_sub_module –with-http_v2_module –with-mail –with-mail_ssl_module –with-stream –with-stream_realip_module –with-stream_ssl_module –with-stream_ssl_preread_module –with-http_v3_module –with-http_quic_module –with-stream_quic_module –with-cc-opt=”-I../modules/boringssl/include $(CFLAGS)” –with-ld-opt=”-L../modules/boringssl/build/ssl -L../modules/boringssl/build/crypto $(LDFLAGS)”

  • Vincent van Adrighem
    Posted May 12, 2021 5:15 pm 0Likes

    Thanks for this howto. It seems to work, although the data is still loaded over http/2…but almost there.
    There’s one step missing though. BoringSSL is downloaded and compiled in a separate directory, but then the debian/rules config change assumes it is in the debian/modules subdirectory, which isn’t true if you follow the howto to the letter.

    Fix: create the debian/modules directory in the nginx-1.19.6 directory and move the whole boringssl into that modules directory.

    This step makes sense if you look at the config file change, and the optional extra modules mentioned in the howto.

  • Dmitry
    Posted June 22, 2021 4:32 pm 0Likes

    boringssl should be cloned into ~/nginx-1.19.6/debian/modules

  • Gwyneth Llewelyn
    Posted February 6, 2022 9:24 pm 0Likes

    Hi!

    I have what seems to be a very absurd question…

    All nginx configuration examples I found via Google (and I’ve been doing that routinely, just in case someone has posted a newer answer) assume that there is just one configured virtual host, bound to all interfaces on the server.

    In my case, I have several virtual hosts (and sometimes bound to just a couple of interfaces), using different domains. All of them have HTTPS enabled, each with their own certificate, all running from the same interface — this works thanks to the latest developments which allow that, as opposed to the old method of ‘one IP address per SSL-enabled virtual host’.

    Well, apparently, while this has long, long ago been dealt with for HTTPS in general, and HTTP/2 in particular, it seems not to work with HTTP/3.

    What happens is that the SSL certificate returned via HTTP/3 will always be the one from the first configured virtual host. Granted, I can change the order, and make sure that the one running HTTP/3 is the first on the list (this is actually what I have done so far). All’s well until you need to add a second host with HTTP/3 — then it will always retrieve the first certificate in the list, and, naturally enough, that certificate will not be valid.

    If all the virtual hosts are under the same subdomain (e.g. a.example.com, b.example.com, etc.) then, of course, these days, Let’s Encrypt can emit a wildcard certificate for the whole domain, also for free. But there is no way to do that for multiple, distinct second-level domains (e.g. example1.com, example2.com). As a consequence, for the time being, I can have one and only one virtual host running HTTP/3 — all the others will simply refuse to work.

    I thought there could be a workaround with the alt-svc tag. Again, all examples I’ve found show that the IP port used on the alt-svc tag is exactly the same as for HTTP/2 — namely, port 443. I thought, if there is actually a setting to tell the browser what IP port ought to be used for HTTP/3, why not make them different? In other words, I could have, say, 10443 for the first virtual host, 11443 for the second, and so forth (that’s just an example; in the real life, I’d have to check that none of these ports are used for other well-known services, but you get the point). This will give me, in theory, 60K+ virtual hosts to play with, more than enough for my case 🙂 (In practice, punching holes through the firewall for all those ports would be utter insanity, but let’s forget that for a second).

    Nginx actually has no problem with this. It accepts something like add_header alt-svc 'h3-29=":20443"; ma=86400'; without complaining. It binds to that port, sure enough, and is happy to take requests there. The firewall(s) also seems to be fine with allowing UDP packets through them to port 20443 (I have made a few tests).

    So far, so good — but when I attempt to connect via curl (modified to use quic + http/3 extensions, using the Cloudflare To-Do document), it completely ignores port 20443 — it goes straight to port 443, disregarding what is configured on the alt-svc header. The same applies to any HTTP/3-capable browser I’ve tested (it’s just harder to debug).

    What am I doing wrong?

    Is this a limitation coming from the compiled-in libraries (that discard whatever port is configured)? Is the HTTP/3 specification still not fixed on this particular detail, and so everyone ‘assumes’ that the only possible port is 443?

    Or is there something else required to get a working configuration for multiple virtual hosts, bound on the same interface, all supporting HTTP/3 as flawlessly as they already support unencrypted HTTP and HTTPS?

    Thanks for any insight 🙂

Leave a Comment