Server Setup

Practical guide: tuning http2_chunk_size in NGINX

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

What http2_chunk_size controls

  • It sets the target size of HTTP/2 DATA frames written per send iteration for a response.
  • Typical effective range is 2k–16k. Values above the peer’s maximum frame size are split into multiple frames; effective frame payload won’t exceed the peer’s max (usually 16k), so “32k/64k” behave like 16k against standard clients.
  • Smaller chunks improve early-byte latency and multiplexing fairness. Larger chunks reduce framing/syscall overhead and can improve bulk throughput.

We’ve run a testing of various settings that vary payload size and http2_chunk_size so you don’t have to.

Quick tuning picks for various payload sizes (based on our measured results)

  • Small/interactive (HTML, small images, ≤64 KB): http2_chunk_size 4k–8k
  • Medium assets (CSS/JS ~100–300 KB, binaries up to ~10 MB): http2_chunk_size 8k–16k
  • Large/static downloads (>10 MB): http2_chunk_size 16k

Combined policy (latency + throughput)

  • Global default: http2_chunk_size 8k; (balanced TTFB and good throughput across sizes)
  • Per-location overrides:
    • Small/interactive (/, /css/, /js/): http2_chunk_size 4k;
    • Medium assets (e.g., /media/, 0.5–10 MB): http2_chunk_size 16k;
    • Large/static downloads (e.g., /downloads/, >10 MB): http2_chunk_size 16k; (note: if your clients accept >16k frames, 32k–64k may help; most browsers cap to 16k frames)

Rule-of-thumb formula for http2_chunk_size

If you’d like a simple sizing rule from payload size S (bytes) to one of {4k, 8k, 16k}:

  • Let S_kb = S / 1024.
  • If S_kb ≤ 64 → 4k
  • Else if S_kb ≤ 2048 → 8k–16k (start with 8k; use 16k if asset is consistently ≥1 MB)
  • Else → 16k

Why: aim to keep the number of chunks per response roughly within 4–64 for typical assets, minimizing overhead without starving other streams.

Per-location examples

The http2_chunk_size is supported in location context, which means you may tune it up as needed.
So armed with knowledge on which locations store which payload sizes on average, the ultimate tuning is setting up the right chunk size in the right location:

server {
    listen 443 ssl;
    http2 on;
    # Baseline
    http2_chunk_size 8k;

    # Small/interactive
    location ~ \.php$ {
        http2_chunk_size 4k;
        fastcgi_pass …;
    }
    location /css/         { http2_chunk_size 4k; }
    location /js/          { http2_chunk_size 8k; }

    # Large/static
    location /downloads/   { http2_chunk_size 16k; }
}

If NGINX terminates TLS in front of Varnish

When NGINX terminates TLS, you usually have a single root location in your TLS side of things:

location / {
    listen 443 ssl;
    http2 on;
    proxy_set_header … ;
    proxy_pass …;
}

You can’t allocate a location \.php$ to set up an optimized chunk size there. However, armed this time with a knowledge that your typical framework does SEO-friendly URLs, you can set up an extra location that targets URIs without extensions. For example /some/article would end up there.

In other words, add a dedicated “extensionless” location for SEO‑friendly URLs (usually dynamic pages going to PHP via Varnish). These responses are typically small; prefer a low chunk size for latency:

upstream varnish { server 127.0.0.1:6081; }

server {
    listen 443 ssl;
    http2 on;

    # Shared proxy headers (inherited by locations)
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # Baseline
    location / {
        http2_chunk_size 8k;
        proxy_pass http://varnish;
    }

    # Extensionless URLs (no dot anywhere in path) → dynamic pages (favor latency)
    location ~ ^/[^.]+$ {
        http2_chunk_size 4k;
        proxy_pass http://varnish;
    }

    # Static assets → larger chunk for throughput
    location ~* \.(?:css|js|png|jpe?g|gif|svg|ico|woff2?)$ {
        http2_chunk_size 16k;
        proxy_pass http://varnish;
    }
}

Notes and caveats

  • http2_chunk_size values above the peer’s frame size are silently clamped; for most clients the effective maximum is 16k.
  • The best value can change with network conditions (Wi‑Fi/4G/long-RTT). Validate on your own network and client mix.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.