A site I was helping with passed every HTTP/2 check, then still failed to serve HTTP/3: the firewall allowed TCP 443 but silently dropped UDP 443. The fix took minutes once we spotted it. In this guide, I’ll show you how to serve both protocols on a VPS with Caddy, open the right ports, and verify what clients actually receive.
What HTTP/2 and HTTP/3 change
HTTP/2 multiplexes requests over a single TCP connection. HTTP/3 uses QUIC over UDP, which can avoid some of the delays caused by packet loss on TCP connections. Neither protocol makes a slow backend fast, but both can improve how browsers fetch resources—especially when a page needs many requests.
You don’t need to configure both protocols separately for every browser. A web server can advertise support, and compatible clients negotiate the best option. HTTP/3 can fall back to HTTP/2 or HTTP/1.1 when UDP is blocked or unsupported.
Before you start
You’ll need:
- A Debian- or Ubuntu-based VPS with a public IP
- A domain name whose DNS points to that IP
- Permission to open inbound TCP and UDP ports
- An application listening on a local port, such as
3000
For a modest website, CPU and network capacity usually matter more than HTTP/3 support alone. PowerVPS is one VPS option worth evaluating for a conventional web workload. Vast.ai is a GPU-rental service, so it’s a better fit when your application needs GPU compute—not as a default place to host a small HTTP server. For comparing hosting approaches and costs, the Server Rental Guide is a useful resource.
Install Caddy
Caddy handles HTTPS certificates automatically and supports HTTP/2 and HTTP/3. That makes it a practical starting point when you want to serve both without maintaining a custom TLS configuration.
On Debian or Ubuntu, install Caddy from its official package repository:
sudo apt-get update
sudo apt-get install -y debian-keyring debian-archive-keyring \
apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt-get update
sudo apt-get install -y caddy
The package sets Caddy up as a system service. Check that it started:
sudo systemctl status caddy
Configure your site
Replace example.com with your domain and 3000 with the local port your app uses. Create or edit /etc/caddy/Caddyfile:
example.com {
reverse_proxy 127.0.0.1:3000
}
Validate the configuration, then reload Caddy:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
Caddy obtains and renews the site’s TLS certificate automatically, provided the domain resolves to this server and the required ports are reachable. HTTP/2 and HTTP/3 are supported by Caddy’s HTTPS server; you generally don’t need to add protocol directives to this basic configuration.
If your application is static, you can serve files directly instead of proxying:
example.com {
root * /var/www/example
file_server
}
Don’t expose the application’s local port publicly unless you have a specific reason. Keeping it bound to 127.0.0.1 means clients reach the app through the proxy, where TLS and protocol negotiation are handled.
Open TCP and UDP 443
HTTPS over HTTP/2 uses TCP 443. HTTP/3 uses UDP 443. Both need to be allowed through your VPS firewall and any provider-level firewall or security group.
With UFW:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status
Port 80 is useful for certificate issuance and redirects. If you use a different firewall, add equivalent inbound rules there. Opening UDP 443 only in UFW won’t help if a cloud firewall still blocks it.
A non-obvious check: confirm both DNS records and firewall layers. A stale AAAA record can send IPv6-capable clients to the wrong place even when the A record is correct. And because HTTP/2 still works when UDP is blocked, a quick browser visit can make a broken HTTP/3 setup look healthy.
Verify what the server serves
First, check the HTTPS response with curl:
curl -I --http2 https://example.com
For HTTP/3, your curl build must include HTTP/3 support. Check with:
curl --version
If the listed features include HTTP3, try:
curl -I --http3-only https://example.com
If your curl lacks HTTP/3 support, use a recent browser’s developer tools or a reputable external HTTP/3 test. A failed --http3-only request doesn’t by itself prove the server is misconfigured; the client may lack support, or UDP may be blocked somewhere along the route.
You can also check Caddy’s service logs while testing:
sudo journalctl -u caddy --since "10 minutes ago"
Troubleshooting and practical trade-offs
If HTTPS does not come up, check that the domain points to the VPS, TCP 80 and 443 are reachable, and no other process is already listening on those ports:
sudo ss -lntup
If HTTP/2 works but HTTP/3 does not, focus on UDP 443. Check the host firewall, provider firewall, and network path. Some corporate networks and public Wi-Fi block UDP; that’s why HTTP/3 should be an additional path, not the only one.
Another practical tip: test from more than one network. A successful test from your home connection doesn’t tell you whether a mobile carrier or office network blocks QUIC. Browsers can fall back, so users may still load the site—but they won’t necessarily use HTTP/3.
Finally, measure before and after. Compare page-load timings or request traces from representative clients, and watch CPU, memory, and error rates under realistic traffic. Protocol support is easy to enable; whether it improves your users’ experience depends on their networks, your workload, and the rest of your delivery stack.
Summary
With Caddy, a domain pointing to your VPS, and TCP plus UDP 443 open, you can serve HTTPS over HTTP/2 and HTTP/3 without hand-managing certificates. Verify HTTP/2 and HTTP/3 with capable clients, check every firewall layer, and test from different networks. HTTP/3 is useful when the path supports it; HTTP/2 fallback keeps the site reachable when it doesn’t.
Top comments (0)