Making FastDL Downloads Fast (Compression, CDN, Caching)

July 2, 2025 Daemon666 8 min read 10 views

A working FastDL mirror already beats the ancient in-game UDP transfer, but "working" and "fast" are different things. A player joining a map with 30 MB of custom content should wait seconds, not a minute. The speed comes from four levers: pre-compressing the files, serving them from a real web server tuned for static files, setting correct MIME types, and putting a CDN in front. Here is how to pull each one.

1. Pre-compress every file with bzip2

The client understands bzip2-compressed downloads natively: if it requests maps/de_rats.bsp and your mirror has maps/de_rats.bsp.bz2, it pulls the smaller compressed file and decompresses locally. A .bsp often shrinks 40–70%, so this is the biggest single win. Compress the whole tree once:

find /var/www/fastdl/cstrike -type f \( -name '*.bsp' -o -name '*.mdl' -o -name '*.wav' -o -name '*.spr' -o -name '*.tga' \) -exec bzip2 -k9 {} \;

-k keeps the original (some clients still fetch the raw file), -9 is maximum compression. Re-run it whenever you add maps. Full detail is in the bzip2 compression guide.

2. Serve from nginx, not the game server

Never point sv_downloadurl at the game box's own trickle transfer. Use nginx (or Apache) tuned for static delivery:

location /cstrike/ {
    root /var/www/fastdl;
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 30;
    gzip off;
}

sendfile on lets the kernel copy the file straight to the socket without buffering through userspace; tcp_nopush batches headers with data; keepalive reuses the connection across the many small files a map references. Leave gzip off for the .bz2 files — they are already compressed and re-compressing wastes CPU.

3. Set the MIME type so browsers and the CDN cache it

If the web server returns the wrong Content-Type, some clients and most CDNs refuse to cache the file. Add the bzip2 type:

types {
    application/x-bzip2  bz2;
}

A missing MIME type is the classic reason a CDN passes every request through to origin instead of serving from cache — the download works but never gets faster.

4. Put Cloudflare in front and cache aggressively

Map and model files never change once published, so they are ideal CDN content. Point sv_downloadurl at a hostname proxied through Cloudflare, then set a Cache Rule that caches the .bz2 extensions at the edge with a long TTL:

Cache-Control: public, max-age=2592000, immutable

The first player from a region pulls from your origin; everyone after pulls from the nearest Cloudflare edge at full line speed. This is what turns a globally slow mirror into a fast one regardless of where your VPS lives.

5. Know the limits you cannot beat

The client downloads FastDL files largely sequentially during the loading screen, so raw per-file latency matters more than total bandwidth — which is exactly why edge caching and keepalive help most. You cannot force parallel downloads from the engine side. The remaining lever is content volume: every custom model, sound and sprite a map references is a file to fetch, and the loading screen cannot finish until the last one arrives. Trimming unused custom content, sharing models between maps, and dropping oversized WAVs down to what the engine actually needs shortens the download more reliably than any server tweak — a 30 MB map that becomes an 18 MB map after removing three unused high-resolution skyboxes downloads faster for every single player, forever.

Common errors

  • Downloads work but stay slow — no .bz2 files on the mirror, so clients pull the full-size originals. Run the compression pass.
  • CDN never caches, every request hits origin — missing or wrong MIME type, or no cache rule for .bz2. Fix the types block and the Cloudflare rule.
  • Some files 404 over HTTP — the cstrike/ path level is missing or doubled in the web root; the client requests <url>/cstrike/maps/... exactly.
  • bz2 present but client still fetches raw file — harmless; you used bzip2 -k so both exist. Confirm the .bz2 is the one being cached.
  • High origin CPU during map change waves — nginx is re-compressing; ensure gzip off and that you serve the pre-made .bz2 directly.

Verification

Open the exact compressed URL in a browser and confirm it downloads and reports the right type:

curl -I https://dl.example.com/cstrike/maps/de_rats.bsp.bz2

Look for Content-Type: application/x-bzip2 and, once behind the CDN, a cf-cache-status: HIT on the second request. Then delete a custom map from a test client, connect, and watch the loading-screen download finish in seconds. If it crawls, you are still serving uncompressed files or the CDN is missing them — recheck steps 1 and 4.

Contributors: Daemon666 ✦
Share: