Tonight’s TV lineup was pure garbage, so I decided to geek out with some server tinkering instead. What better way to spend the evening than diving into a protocol-level performance upgrade?
Today, I want to talk about something that improves performance “under the hood.” Specifically, how data is transmitted between the browser and the webserver. You might have already heard about HTTP/3 – the shiny new transport protocol that promises faster, more reliable page loads. Benchmarks suggest improvements of around 10–15% in real-world scenarios, especially noticeable on mobile devices.
Why Switch?
Until now, my WordPress blog was powered by the good old Apache
webserver. Reliable, mature, but unfortunately still lagging behind when it comes to HTTP/3 support. This standard is no longer “bleeding edge” – it’s here, and browsers (Chrome, Firefox, Safari, Edge) already speak it fluently. Apache, however, has been slow to catch up.
So, the logical next step: migrate to Nginx, which offers HTTP/3 support. Of course, that meant porting my Apache configuration into Nginx syntax – a nerdy puzzle in itself.
The Migration Steps
- Enable the official Nginx repository
Debian 12’s default repos don’t yet include a build with HTTP/3 support. To get QUIC and HTTP/3, I had to add the vendor repo directly. - Install Nginx
Straightforward, but crucial. Without the vendor repo, you’d end up with an outdated package. - Migrate configs
Apache’s.conf
syntax ≠ Nginx syntax. Things like.htaccess
rules need manual translation intoserver
blocks andlocation
directives. - Switch services
Shut down Apache, disable it from autostart, then enable and start Nginx. You don’t want both listening on port 80/443. - Adjust PHP-FPM user
Under Apache, the user waswww-data
. For Nginx, the process typically runs asnginx
, so I had to update permissions accordingly. - Fix security headers
One of my hardened HTTP headers clashed with the new setup. A quick tweak fixed the handshake issues. - Test, test, test
Because in sysadmin life, if you haven’t tested it from three different browsers and your phone over a sketchy Wi-Fi, it doesn’t really work.
The Result
Now my blog is happily serving pages over HTTP/3 (QUIC). Page loads feel a tad snappier, especially when switching between posts or reloading with a shaky connection. It’s one of those upgrades you don’t notice when it’s there, but you definitely feel the pain when it’s missing.
Here is the log from my NGINX webserver showing client requests. You can now clearly see HTTP/3 in action :
79.246.98.138 - - [22/Aug/2025:19:28:19 +0000] "GET /wp-content/uploads/2025/08/hangman.webp HTTP/3.0" 200 341646 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:28:19 +0000] "GET /wp-content/uploads/2025/08/vpn_broken.png HTTP/3.0" 200 927519 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:28:19 +0000] "GET /page/2/ HTTP/3.0" 200 13150 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/05/cropped-superman-1-1.png HTTP/3.0" 200 9032 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/08/chaos-300x300.webp HTTP/3.0" 200 12818 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/08/rocket-300x300.webp HTTP/3.0" 200 10792 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/08/bootme-300x200.webp HTTP/3.0" 200 15418 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/08/hangman-300x300.webp HTTP/3.0" 200 17030 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2025/05/rack-205x300.jpg HTTP/3.0" 200 19892 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2024/09/grapheneos-135x300.png HTTP/3.0" 200 22647 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:23 +0000] "GET /wp-content/uploads/2024/11/protectli-300x150.png HTTP/3.0" 200 22307 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" 79.246.98.138 - - [22/Aug/2025:19:40:24 +0000] "GET /wp-content/cache/wpo-minify/1755704871/assets/wpo-minify-header-9e1552c9.min.css HTTP/3.0" 200 510477 "https://www.apt-upgrade.me/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0)
Final Thoughts
Was it worth the hassle? Absolutely. Migrating from Apache to Nginx wasn’t just about getting HTTP/3 – it also gave me finer-grained control over caching, reverse proxying, and SSL/TLS configurations. Plus, it scratched that late-night nerd itch.
Next step on my list: tuning QUIC parameters for even faster connection establishment and experimenting with 0-RTT resumption. But that’s a story for another sleepless evening.