{"title":"Serving Precompressed Brotli And Gzip From Nginx","html_url":"https://deallocatedobjects.com/posts/serving-precompressed-brotli-and-gzip-from-nginx","source_url":"","markdown_content":"Every text file on Deallocated Objects is compressed before it reaches the server. The generator writes `index.html`, then writes `index.html.br` and `index.html.gz` right next to it, and nginx picks whichever one the browser asked for. The homepage goes over the wire at about 14 KB instead of about 61 KB, and nginx does no compression work to get there.\n\n### The Build Step\n\nThe last thing `src/build.ts` does is walk the output folder and compress the text files. Bun ships `node:zlib` with Brotli built in, so there's no extra package:\n\n```ts title=\"src/build.ts\"\nconst { brotliCompressSync, gzipSync, constants: Z } = await import(\"node:zlib\");\n\n// ---------- precompress (nginx gzip_static + brotli_static serve these) ----------\nlet compressed = 0;\n(function walk(d: string) {\n  for (const f of rd(d)) {\n    const full = join(d, f);\n    if (st(full).isDirectory()) { walk(full); continue; }\n    if (!/\\.(html|css|js|json|xml|atom|txt|md|svg|xsl|webmanifest)$/.test(f) || st(full).size < 1024) continue;\n    const buf = rf(full);\n    wf(`${full}.br`, brotliCompressSync(buf, { params: { [Z.BROTLI_PARAM_QUALITY]: 11, [Z.BROTLI_PARAM_SIZE_HINT]: buf.length } }));\n    wf(`${full}.gz`, gzipSync(buf, { level: 9 }));\n    compressed++;\n  }\n})(dist);\n```\n\nImages and fonts stay off the extension list because they're already compressed, and files under 1 KB aren't worth the trouble. The build deletes `dist/` before it writes anything and the deploy uses `rsync --delete`, so a `.br` can't go stale next to a newer page.\n\n### Why Quality 11 Belongs At Build Time\n\nHere's the JSON Feed (443,921 bytes) at a few settings, averaged over 10 runs. My Mac was busy when I timed these, so look at the ratios more than the milliseconds.\n\n| Setting | Size | Time |\n|---|---|---|\n| gzip level 9 | 81,072 | 21 ms |\n| Brotli quality 5 | 75,120 | 19 ms |\n| Brotli quality 9 | 72,616 | 36 ms |\n| Brotli quality 11 | 65,517 | 1,746 ms |\n\nQuality 11 takes about 50 times the CPU of quality 9 to shave off another 10%. Nobody should pay that per request, but paying it once on my Mac at build time means every reader gets the smaller file.\n\n### The Nginx Side\n\nUbuntu 24.04 packages Brotli as a dynamic module for its stock nginx, so `apt install libnginx-mod-http-brotli-static` and a reload is the whole setup. Then the vhost turns on both:\n\n```nginx title=\"deploy/nginx.conf\"\ngzip_static on;\nbrotli_static on;\ngzip_vary on;\n```\n\nIf the request's `Accept-Encoding` includes `br` and a `.br` file exists, nginx sends that. Otherwise it tries the `.gz`, and then the plain file. This works through `try_files` too, so `/posts/<slug>` resolves to its `.html` and picks up the compressed files next to it. Because these are real files on disk, responses get a normal `Content-Length`, and `gzip_vary` adds the `Vary: Accept-Encoding` that keeps shared caches from handing Brotli bytes to a client that can't read them.\n\nI keep the `.gz` files because browsers aren't the only readers. Feed readers, crawlers and scripts often ask only for gzip.\n\n### One Gotcha With Proxies\n\nWhile testing I noticed this:\n\n```bash\n$ curl -sI -H 'Accept-Encoding: gzip' -H 'Via: 1.1 proxy' https://deallocatedobjects.com/ | grep -i content-length\ncontent-length: 60621\n$ curl -sI -H 'Accept-Encoding: br' -H 'Via: 1.1 proxy' https://deallocatedobjects.com/ | grep -i content-length\ncontent-length: 13945\n```\n\n`gzip_static` follows nginx's `gzip_proxied` setting, which defaults to `off`, so a request carrying a `Via` header gets the uncompressed file. The Brotli module doesn't check it. Nothing sits in front of this site, but if you put a CDN or proxy in front of nginx that adds `Via`, set `gzip_proxied any` or your gzip clients will quietly get full-size files.","html_content":"<p>Every text file on Deallocated Objects is compressed before it reaches the server. The generator writes <code>index.html</code>, then writes <code>index.html.br</code> and <code>index.html.gz</code> right next to it, and nginx picks whichever one the browser asked for. The homepage goes over the wire at about 14 KB instead of about 61 KB, and nginx does no compression work to get there.</p>\n<h2 id=\"the-build-step\" data-level=\"3\">The Build Step</h2>\n<p>The last thing <code>src/build.ts</code> does is walk the output folder and compress the text files. Bun ships <code>node:zlib</code> with Brotli built in, so there’s no extra package:</p>\n<figure class=\"code\" data-lang=\"typescript\"><figcaption><span class=\"file\">src/build.ts</span><span class=\"lang\">TypeScript</span></figcaption><pre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> { </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">brotliCompressSync</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">gzipSync</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">constants</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">Z</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> } </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> await</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> import</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"node:zlib\"</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">// ---------- precompress (nginx gzip_static + brotli_static serve these) ----------</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">let</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> compressed </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> 0</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">function</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> walk</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">d</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> string</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">  for</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> f</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> of</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> rd</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(d)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    const</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> full</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> join</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(d, f);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    if</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">st</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(full).</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">isDirectory</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">()) { </span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">walk</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(full); </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">continue</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">; }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    if</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">!</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">/</span><span style=\"--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold\">\\.</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">(html</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">css</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">js</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">json</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">xml</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">atom</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">txt</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">md</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">svg</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">xsl</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">|</span><span style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">webmanifest)</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">$</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">/</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">.</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">test</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(f) </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">||</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> st</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(full).size </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">&#x3C;</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> 1024</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">) </span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">continue</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    const</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> buf</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> rf</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(full);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">    wf</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">`${</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">full</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">}.br`</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">brotliCompressSync</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(buf, { params: { [</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">Z</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">.</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">BROTLI_PARAM_QUALITY</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">]: </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">11</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, [</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">Z</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">.</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">BROTLI_PARAM_SIZE_HINT</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">]: buf.</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">length</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> } }));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">    wf</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">`${</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">full</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">}.gz`</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">gzipSync</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(buf, { level: </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">9</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> }));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    compressed</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">++</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">})(dist);</span></span></code></pre></figure><p>Images and fonts stay off the extension list because they’re already compressed, and files under 1 KB aren’t worth the trouble. The build deletes <code>dist/</code> before it writes anything and the deploy uses <code>rsync --delete</code>, so a <code>.br</code> can’t go stale next to a newer page.</p>\n<h2 id=\"why-quality-11-belongs-at-build-time\" data-level=\"3\">Why Quality 11 Belongs At Build Time</h2>\n<p>Here’s the JSON Feed (443,921 bytes) at a few settings, averaged over 10 runs. My Mac was busy when I timed these, so look at the ratios more than the milliseconds.</p>\n<table>\n<thead>\n<tr>\n<th>Setting</th>\n<th>Size</th>\n<th>Time</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>gzip level 9</td>\n<td>81,072</td>\n<td>21 ms</td>\n</tr>\n<tr>\n<td>Brotli quality 5</td>\n<td>75,120</td>\n<td>19 ms</td>\n</tr>\n<tr>\n<td>Brotli quality 9</td>\n<td>72,616</td>\n<td>36 ms</td>\n</tr>\n<tr>\n<td>Brotli quality 11</td>\n<td>65,517</td>\n<td>1,746 ms</td>\n</tr>\n</tbody>\n</table>\n<p>Quality 11 takes about 50 times the CPU of quality 9 to shave off another 10%. Nobody should pay that per request, but paying it once on my Mac at build time means every reader gets the smaller file.</p>\n<h2 id=\"the-nginx-side\" data-level=\"3\">The Nginx Side</h2>\n<p>Ubuntu 24.04 packages Brotli as a dynamic module for its stock nginx, so <code>apt install libnginx-mod-http-brotli-static</code> and a reload is the whole setup. Then the vhost turns on both:</p>\n<figure class=\"code\" data-lang=\"nginx\"><figcaption><span class=\"file\">deploy/nginx.conf</span><span class=\"lang\">nginx</span></figcaption><pre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">gzip_static </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">on</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">brotli_static</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> on</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">gzip_vary </span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">on</span><span style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">;</span></span></code></pre></figure><p>If the request’s <code>Accept-Encoding</code> includes <code>br</code> and a <code>.br</code> file exists, nginx sends that. Otherwise it tries the <code>.gz</code>, and then the plain file. This works through <code>try_files</code> too, so <code>/posts/&lt;slug&gt;</code> resolves to its <code>.html</code> and picks up the compressed files next to it. Because these are real files on disk, responses get a normal <code>Content-Length</code>, and <code>gzip_vary</code> adds the <code>Vary: Accept-Encoding</code> that keeps shared caches from handing Brotli bytes to a client that can’t read them.</p>\n<p>I keep the <code>.gz</code> files because browsers aren’t the only readers. Feed readers, crawlers and scripts often ask only for gzip.</p>\n<h2 id=\"one-gotcha-with-proxies\" data-level=\"3\">One Gotcha With Proxies</h2>\n<p>While testing I noticed this:</p>\n<figure class=\"code\" data-lang=\"bash\"><figcaption><span class=\"lang\">Shell</span></figcaption><pre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">$</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> curl</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -sI</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -H</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> 'Accept-Encoding: gzip'</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -H</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> 'Via: 1.1 proxy'</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> https://deallocatedobjects.com/</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> |</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> grep</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -i</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> content-length</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">content-length:</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> 60621</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">$</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> curl</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -sI</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -H</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> 'Accept-Encoding: br'</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -H</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> 'Via: 1.1 proxy'</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> https://deallocatedobjects.com/</span><span style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> |</span><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> grep</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> -i</span><span style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> content-length</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">content-length:</span><span style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> 13945</span></span></code></pre></figure><p><code>gzip_static</code> follows nginx’s <code>gzip_proxied</code> setting, which defaults to <code>off</code>, so a request carrying a <code>Via</code> header gets the uncompressed file. The Brotli module doesn’t check it. Nothing sits in front of this site, but if you put a CDN or proxy in front of nginx that adds <code>Via</code>, set <code>gzip_proxied any</code> or your gzip clients will quietly get full-size files.</p>","created_at":"2026-11-08T14:00:00.000Z","updated_at":"2026-11-08T14:00:00.000Z"}