--- title: "Hosting FastDL Behind Cloudflare (Free CDN)" excerpt: "Put your CS 1.6 FastDL mirror behind Cloudflare's free CDN: point sv_downloadurl at a proxied domain, force caching of .bsp and .bz2 with a Cache Rule, and avoid the file-type caching trap." category: "administration" tags: "fastdl, cloudflare" image: "" read_time: "8 min read" ---
If your FastDL host is a single VPS, every joining player pulls maps straight from it — fine until a popular map change sends fifty clients at your origin at once. Putting the mirror behind Cloudflare's free CDN caches those static files at the edge, so the second and every subsequent client downloads from a nearby Cloudflare node instead of hammering your box. It is free and effective, but Cloudflare does not cache .bsp and .bz2 by default, and that trap is why most \"Cloudflare FastDL\" attempts see no benefit.
Cloudflare sits in front of a real web server — it does not host files itself. Get a normal FastDL mirror working first, with the correct cstrike/ path layout and, ideally, bzip2-compressed files. Confirm the origin serves a map over plain HTTP before adding the CDN. Cloudflare cannot fix a broken origin path; it will just cache the 404.
Add your domain to Cloudflare, then create a DNS record for the FastDL subdomain pointing at your origin IP, with the proxy (orange cloud) on:
Type Name Content Proxy A dl 203.0.113.10 Proxied (orange cloud)
Proxied is what routes traffic through Cloudflare's edge and enables caching. A \"DNS only\" (grey cloud) record points clients straight at your origin and gives you no CDN at all. Then set sv_downloadurl to the proxied hostname:
// server.cfg sv_downloadurl "http://dl.example.com/cs"
HTTPS works too and Cloudflare gives you a certificate; just make sure the origin also answers so Cloudflare can fetch and cache.
Cloudflare's default cache only covers a fixed list of static extensions — images, CSS, JS — and .bsp, .bz2, .mdl, .wav, .spr are not on it. Left alone, Cloudflare passes every game-file request through to your origin and caches nothing, defeating the whole exercise. Fix it with a Cache Rule (the modern replacement for Page Rules) that forces these files into the cache. In the dashboard under Caching → Cache Rules, create a rule:
When incoming requests match:
(http.request.uri.path.extension in {"bsp" "bz2" "mdl" "wav" "spr"})
Then:
Cache eligibility: Eligible for cache
Edge TTL: override, e.g. 7 days
Alternatively, match the whole path prefix — http.request.uri.path contains "/cstrike/" — and mark it eligible. Either way you are telling Cloudflare \"these are static, cache them,\" which it will not assume on its own.
The proof is the cf-cache-status response header. Request a map file twice and watch it go from MISS (fetched from origin) to HIT (served from edge):
curl -sI https://dl.example.com/cs/cstrike/maps/de_rats.bsp.bz2 | grep -i cf-cache # first: cf-cache-status: MISS # second: cf-cache-status: HIT
If it stays MISS or shows DYNAMIC, your Cache Rule is not matching — recheck the extension list and that the rule is enabled.
cf-cache-status is DYNAMIC/MISS: the game extensions are not covered by a Cache Rule. Add the rule; Cloudflare does not cache .bsp/.bz2 by default.cstrike/ level). Cloudflare faithfully proxies the 404; fix the mirror layout.Error 5xx from Cloudflare. Confirm the origin answers and the certificate is valid.End to end: request a map file through the Cloudflare hostname twice and confirm the second is a cache HIT (above). Then join the server from a client that lacks the map and watch it download quickly; check your origin's access log and confirm most requests after the first are absent because the edge is serving them. Keep the origin mirror and its bzip2 files in sync with your rotation, and remember to purge the Cloudflare cache whenever you replace a file under an existing name.