Skip to content

2026-10-30 OVH1 101 (proxy) enable HTTP/3 for beta.openfoodfacts.net#

In order to enabling HTTP/3 for the first canary, it was was enabled for the OFF Explorer staging deployment. Following a guide from PB was followed. In the 101 LXC container, the QUIC key was generated and excluded from etckeeper.

$ mkdir -p /etc/nginx/ssl/
$ openssl rand -base64 32 > /etc/nginx/ssl/quic_host.key
$ chmod 400 /etc/nginx/ssl/quic_host.key
$ chown www-data:www-data /etc/nginx/ssl/quic_host.key
$ echo "nginx/ssl/quic_host.key" >> /etc/.gitignore
$ git add /etc/.gitignore
$ git commit -m "chore: Ignore QUIC key from git"

Created a new reusable HTTP/3 config at /opt/openfoodfacts-infrastructure/confs/ovh1-reverse-proxy/nginx/conf.d/http3.conf, and created a new symlink

http3_stream_buffer_size 1m;
quic_retry on;
ssl_early_data off;
quic_gso off;

quic_host_key /etc/nginx/ssl/quic_host.key;
$ ln -s  /opt/openfoodfacts-infrastructure/confs/ovh1-reverse-proxy/nginx/conf.d/http3.conf /etc/nginx/conf.d/http3.conf

and updated the nginx/sites-enabled/beta.openfoodfacts.net.conf to enable HTTP/3:

diff --git a/confs/ovh1-reverse-proxy/nginx/sites-enabled/beta.openfoodfacts.net.conf b/confs/ovh1-reverse-proxy/nginx/sites-enabled/beta.openfoodfacts.net.conf
index 8f1054a16..32ab3a7fd 100644
--- a/confs/ovh1-reverse-proxy/nginx/sites-enabled/beta.openfoodfacts.net.conf
+++ b/confs/ovh1-reverse-proxy/nginx/sites-enabled/beta.openfoodfacts.net.conf
@@ -6,6 +6,7 @@ map $uri $beta_off_net_basic_auth_setting {

 server {
     listen 443 ssl; # managed by Certbot

+    listen 443 quic reuseport;
     ssl_certificate /etc/letsencrypt/live/beta.openfoodfacts.net/fullchain.pem; # managed by Certbot
     ssl_certificate_key /etc/letsencrypt/live/beta.openfoodfacts.net/privkey.pem; # managed by Certbot
     include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
@@ -14,6 +15,9 @@ server {
     server_name  beta.openfoodfacts.net;

     http2 on;

+    http3 on;
+
+    add_header Alt-Svc 'h3=":443"; ma=3600, h3-29=":443"; ma=3600' always;

     # off:off basic username to avoid indexation by bots (it's staging env)
     auth_basic_user_file /etc/nginx/off.htpasswd;

Allowed UDP 443 in the firewall:

$ iptables -A INPUT -p udp -m udp --dport 443 -j ACCEPT
$ ip6tables -A INPUT -p udp -m udp --dport 443 -j ACCEPT

From another host, the connection was tested successfully using docker run -it --rm ymuski/curl-http3 curl -ILv https://beta.openfoodfacts.net/health --http3-only, http3check, and http3-test.

I tested without the Alt-Svc header first, as asked for in https://github.com/openfoodfacts/openfoodfacts-infrastructure/issues/679#issue-5292086065. For each nginx config change, I first ran nginx -t and upon success systemctl reload nginx.service.

The UDP firewall rules were manually added to /etc/iptables/rules.v4 and /etc/iptables/rules.v6, as no other management tool seems to be in place.

Rollout checklist progress (OVH1 CT 101 canary)#

Scope: only OVH1 LXC container 101 (reverse proxy) for beta.openfoodfacts.net was checked/performed. No access to Scaleway, KS1, or other edges. Baseline items were not performed and are intentionally omitted from this report.

Verify the runtime on every edge#

  • [x] (OVH1/101 only) Runtime checked on the canary edge; no access to Scaleway/KS1.
  • [x] (OVH1/101) nginx -V contains --with-http_v3_module.
  • [x] (OVH1/101) TLS 1.3 enabled and NGINX uses the expected OpenSSL runtime.
  • [x] (OVH1/101) Kernel supports SO_REUSEPORT (native in PVE 6 kernel).
  • [X] Keep 0-RTT, QUIC GSO, and QUIC eBPF disabled for the initial rollout

Open UDP/443#

  • [ ] Add a protocol-specific iptables_public_udp_ports variable to the Ansible firewall role — not done (manual change on OVH1/101 only).
  • N/A Generate IPv4 and IPv6 UDP rules from that variable (Ansible path not used).
  • N/A Set iptables_public_udp_ports: [443] for scaleway-proxy (no access).
  • [x] (OVH1/101, deviation from checklist) UDP/443 opened via iptables/ip6tables on CT 101 and persisted manually in /etc/iptables/rules.v4 and /etc/iptables/rules.v6; not via effective OVH nftables config / repo safe reload procedure.
  • N/A Audit and open UDP/443 in the KS1 host firewall (no access).
  • [~] End-to-end UDP/443 verification: external connection from the Internet (browser/client) was successful; Proxmox firewall was not changed (only CT 101) and appeared not relevant. Provider firewall/security groups, full Proxmox host/container firewall audit, routing/NAT, and both address families were not fully verified as a checklist audit.
  • [ ] Verify that current fail2ban actions also block abusive QUIC clients — not checked.

QUIC key and NGINX configuration#

  • [x] (OVH1/101) Persistent host-local QUIC key created at /etc/nginx/ssl/quic_host.key (deviation from checklist path /etc/nginx/quic_host.key), mode 400 (chmod 400), owner www-data:www-data, excluded from etckeeper/Git via /etc/.gitignore; not committed. Other edges: no access.
  • [x] Key generated once manually (openssl rand -base64 32); no automation guard/“only when absent” logic documented.
  • [x] quic_host_key /etc/nginx/ssl/quic_host.key; and quic_retry on; configured (in http3.conf).
  • [x] (OVH1/101, IPv4) QUIC listener added alongside TCP: listen 443 quic reuseport; and http3 on; in beta.openfoodfacts.net vhost; http2 on; retained. No IPv6 QUIC listener added.
  • [x] nginx -t run before each config change/reload; complete assembled config tested as part of reload workflow.

Canary without browser advertisement#

  • [x] QUIC listener enabled on beta.openfoodfacts.net without Alt-Svc first.
  • [x] UDP socket confirmed with ss -lunp:

    root@proxy:/# ss -lunp
    State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
    UNCONN  0       0       0.0.0.0:443         0.0.0.0:*          users:(("nginx",pid=30535,fd=93),("nginx",pid=30534,fd=93),("nginx",pid=30533,fd=93),("nginx",pid=30532,fd=93),("nginx",pid=29172,fd=93))
    UNCONN  0       0       0.0.0.0:443         0.0.0.0:*          users:(("nginx",pid=30535,fd=92),("nginx",pid=30534,fd=92),("nginx",pid=30533,fd=92),("nginx",pid=30532,fd=92),("nginx",pid=29172,fd=92))
    UNCONN  0       0       0.0.0.0:443         0.0.0.0:*          users:(("nginx",pid=30535,fd=91),("nginx",pid=30534,fd=91),("nginx",pid=30533,fd=91),("nginx",pid=30532,fd=91),("nginx",pid=29172,fd=91))
    UNCONN  0       0       0.0.0.0:443         0.0.0.0:*          users:(("nginx",pid=30535,fd=90),("nginx",pid=30534,fd=90),("nginx",pid=30533,fd=90),("nginx",pid=30532,fd=90),("nginx",pid=29172,fd=90))
    
  • [x] External HTTP/3 test succeeded (docker run -it --rm ymuski/curl-http3 curl -ILv https://beta.openfoodfacts.net/health --http3-only, http3check, http3-test).
  • [x] HTTP/2 and HTTP/1.1 over TCP still work (checked).
  • [x] Client fallback when UDP/443 blocked: UDP was temporarily blocked on the server again; fallback in Edge on the client occurred without noticeable delay.
  • [x] Alt-Svc added for beta responses (deviation from checklist value): add_header Alt-Svc 'h3=":443"; ma=3600, h3-29=":443"; ma=3600' always;.
  • [~] Header verification partially checked (not fully verified across HTML, immutable assets, redirects, errors, and all locations that define their own add_header directives).
  • [X] HTTP/3 usage successfully recorded in logs ($http3 / $server_protocol or equivalent).
  • [ ] No multi-day monitoring documented (NGINX QUIC errors, worker stability, CPU, memory, UDP drops, latency, fallback behavior).

Expand to static/image hosts#

  • [ ] (Out of scope) Enable/advertise HTTP/3 for shared static.openfoodfacts.net / images.openfoodfacts.net vhost — not done (beta canary only).
  • [ ] ma policy deviation: beta already uses ma=86400 (no staged ma=3600 → ma=86400 documented).
  • N/A Scaleway static.openfoodfacts.org host-based advertisement — no access / not done.
  • N/A Verify add_header inheritance in every relevant location on other edges — no access / not done.
  • N/A images.openfoodfacts.org KS1 rollout — no access / not done.
  • N/A KS1 Alt-Svc inheritance in local-file and @proxy2 paths — no access / not done.
  • N/A KS1 image/cache/range/404/large-image tests — no access / not done.

Monitoring#

  • N/A/No External HTTP/3-only probe per enabled hostname (canary scope).
  • N/A/No Normal TCP HTTPS availability probe retained as primary check (no monitoring change documented).
  • N/A/No Alerts for UDP/443 down, HTTP/3-only failures, or missing Alt-Svc.
  • N/A/No Separate IPv4/IPv6 probes.
  • N/A/No Protocol share/performance comparison before/after.