Tech Expert & Vibe Coder

With 14+ years of experience, I specialize in self-hosting, AI automation, and Vibe Coding – building applications using AI-powered tools like Google Antigravity, Dyad, and Cline. From homelabs to enterprise solutions.

Optimizing Sub-20kb Static Sites on Caddy:  Hosting Ultra-lightweight Browser Games and Tools with Brotli Compression and Http/3

Why I Started Obsessing Over File Size

I host a collection of small browser-based tools and games on my Proxmox setup. These aren't complex web apps—they're single-page utilities like a hex color picker, a simple timer, and a few JavaScript games I wrote for fun. The entire collection sits under 20KB per tool, sometimes under 10KB.

I wanted them to load instantly. Not "fast enough"—actually instant. The kind of speed where you click a link and the page is just there. No spinners, no layout shifts, nothing. For files this small, there's no excuse for delay.

That pushed me to look hard at how Caddy serves static files, and specifically at compression and protocol choices that actually matter at this scale.

My Caddy Setup for Tiny Static Sites

I run Caddy in a Docker container on Proxmox. The sites themselves are just HTML, CSS, and vanilla JavaScript—no build step, no frameworks. Each tool is a single directory with an index.html file and maybe a small CSS or JS file inline or separate.

My Caddyfile for these tools looks like this:

tools.vipinpg.com {
    root * /srv/tools
    encode zstd gzip
    file_server
    header {
        Cache-Control "public, max-age=31536000, immutable"
        X-Content-Type-Options "nosniff"
    }
}

I initially used gzip only. Then I tested Brotli. Then I switched to zstd because Caddy supports it and I wanted to see if it made a difference at this file size.

What Worked: Brotli and HTTP/3

Brotli compression made a noticeable difference even on files under 20KB. A 12KB HTML file with inline CSS compressed down to around 3KB with Brotli at level 6. Gzip got it to about 4KB. That's a 25% improvement, which matters when you're trying to fit everything in the first TCP packet.

I enabled Brotli by changing my encode directive:

encode brotli gzip

Caddy handles content negotiation automatically, so browsers that support Brotli get it, others fall back to gzip.

HTTP/3 was already enabled by default in Caddy. I didn't have to configure anything. I tested it using Chrome's network inspector and confirmed QUIC was being used. The difference was subtle on my local network, but over my mobile connection the reduction in handshake time was real. Pages loaded faster on the first visit, especially on flaky connections.

Measuring the Impact

I used Chrome DevTools and curl to measure transfer sizes and timing. For a 15KB tool page:

  • Uncompressed: 15KB transferred
  • Gzip: 5.2KB transferred
  • Brotli: 3.8KB transferred

Time to first byte stayed under 50ms on my local network. Over LTE, HTTP/3 shaved off about 100-150ms compared to HTTP/2 on the initial connection.

What Didn't Work

I tried zstd compression thinking it might be faster or smaller. It wasn't. File sizes were comparable to Brotli, but browser support is nonexistent outside of very recent builds. I removed it because there was no practical benefit.

I also experimented with aggressive caching headers and service workers to make repeat visits instant. The caching worked fine, but service workers added complexity I didn't need. For tools this small, the network request is already fast enough that caching beyond standard HTTP headers felt like over-engineering.

I tried inlining everything into a single HTML file to eliminate extra requests. This worked for some tools, but made editing annoying. I settled on inlining critical CSS and keeping JavaScript separate only when it was reused across multiple tools.

The Docker Container

My Dockerfile is minimal:

FROM caddy:2-alpine
COPY tools/ /srv/tools
COPY Caddyfile /etc/caddy/Caddyfile
EXPOSE 80 443

I mount the tools directory as a volume during development so I can edit files without rebuilding the container. In production, I bake the files into the image.

Caddy handles HTTPS automatically via Let's Encrypt. I didn't have to configure certificates or renewal. It just works.

Key Takeaways

Brotli compression is worth enabling even for very small files. The size reduction is meaningful when you're trying to fit a page in a single round trip.

HTTP/3 helps on mobile and unstable connections. On a stable local network, the difference is negligible, but it's free to enable with Caddy.

For sub-20KB sites, the biggest wins come from eliminating unnecessary requests and keeping the HTML small. Compression and protocol improvements help, but they're secondary to just writing less code.

Caddy makes this setup trivial. The entire configuration is a few lines, and everything else—HTTPS, HTTP/3, compression negotiation—happens automatically.