HttpArena
Knowledge BaseFrameworks

Static File Serving Benchmark (HTTP/2): Implementation Guide

File contents may be served from memory, but the cache must be the framework's own - a documented static file handler with whatever caching it comes with. A cache assembled in the entry does not count: no reading the directory into a map at startup, no pre-loaded buffers, no mapping the files by hand. The cache must also follow the disk: replace a file and the next response must carry the new bytes. Compression must use the framework's standard middleware or built-in static file handler - no handmade compression code. Serving the pre-compressed .br/.gz variants that sit on disk next to the originals is allowed, by a documented framework API (e.g. ASP.NET MapStaticAssets, nginx gzip_static / brotli_static, Caddy precompressed, Hono serveStatic({ precompressed: true })) where the framework has one, and otherwise by selecting the variant in the entry off Accept-Encoding. Those bytes already exist, so choosing one is a file read rather than compression - which is why this is allowed while compressing by hand is not. The response must carry the original file's Content-Type, the matching Content-Encoding, and must still follow the disk.
File contents may be served from memory, the same rule as Standard: the cache must be the framework's own rather than one assembled in the entry, and it must follow the disk - replace a file and the next response must carry the new bytes. Pre-rendered response headers stay allowed, as does serving pre-compressed .gz/.br variants from disk via any mechanism, with any compression approach.
File contents may be served from memory, the same rule as Standard: the cache must be the framework's own rather than one assembled in the entry, and it must follow the disk - replace a file and the next response must carry the new bytes. No other restrictions. Ranked separately from frameworks.
Configuration is free, including sendfile, open_file_cache, mmap and in-memory caching - serving files fast from a tuned cache is the job. Pre-compressed .br/.gz variants may be served through the server's own mechanism (nginx gzip_static / brotli_static, Caddy precompressed). Bodies must come from the mounted static directory; a handler that synthesizes them is not serving files. HTTP/2 must be the server's own implementation, with stream and window tuning allowed.

Serves 20 static files of various types and sizes over HTTP/2 with TLS, simulating a realistic browser page load with multiplexed streams.

Connections: 256, 1,024 Concurrent streams per connection: 32

Workload

The load generator (h2load) requests 20 static files in a round-robin pattern across multiplexed streams. All requests include Accept-Encoding: br;q=1, gzip;q=0.8.

  • CSS (5 files, 8–55 KB): reset.css, layout.css, theme.css, components.css, utilities.css
  • JavaScript (5 files, 15–400 KB): analytics.js, helpers.js, app.js, vendor.js, router.js
  • HTML (2 files, 5–8 KB): header.html, footer.html
  • Fonts (2 files, 20–25 KB): regular.woff2, bold.woff2
  • SVG (2 files, 12–55 KB): logo.svg, icon-sprite.svg
  • Images (3 files, 15–120 KB): hero.webp, thumb1.webp, thumb2.webp
  • JSON (1 file, 3 KB): manifest.json

Total payload: ~1.16 MB across 20 files (~966 KB compressible text + ~200 KB binary).

Pre-compressed versions (.gz, .br) are available on disk. See the Static TLS compression section for full compression rules.

What it measures

  • Static file serving throughput over HTTP/2
  • HTTP/2 multiplexing efficiency with varied response sizes
  • Content-Type handling for different file types
  • File serving strategy efficiency - how well the framework moves bytes to the client (sendfile, readahead, buffer sizing, and its own caching where it has any)
  • TLS overhead with realistic mixed payloads
  • Compression efficiency (optional - reduces I/O at the cost of CPU)

Expected request/response

GET /static/reset.css HTTP/2
HTTP/2 200 OK
Content-Type: text/css

(file contents)
GET /static/app.js HTTP/2
HTTP/2 200 OK
Content-Type: application/javascript

(file contents)

How it differs from baseline-h2

Baseline (HTTP/2)Static Files (HTTP/2)
EndpointSingle GET /baseline220 different /static/* URIs
Response size~2 bytes3–400 KB (varied)
Content typestext/plainCSS, JS, HTML, fonts, SVG, WebP, JSON
h2load modeSingle URIMulti-URI (-i flag, round-robin)

Parameters

ParameterValue
Endpoint20 URIs under /static/*
Connections256, 1,024
Streams per connection32 (-m 32)
Duration5s
Runs3 (best taken)
Load generatorh2load with -i (multi-URI)