Without a FastDL host, every client that lacks your custom maps downloads them over the ancient in-game UDP channel at a few KB/s — a 20 MB map empties the server on rotation. FastDL points clients at a normal web server instead. Nginx is the ideal front end for it: fast, tiny, and trivial to configure for static files. This sets one up correctly, including the path structure that trips everyone up.
1. Install Nginx
apt update apt install -y nginx
Confirm it serves by opening the server IP in a browser; you should get the Nginx welcome page.
2. Create a web root that mirrors cstrike
The single most important rule of FastDL: the engine appends the game-relative path including the cstrike/ folder. A client that needs maps/de_rats.bsp requests <downloadurl>/cstrike/maps/de_rats.bsp. So your web root must contain a cstrike/ directory that mirrors the server's:
mkdir -p /var/www/fastdl/cstrike/{maps,models,sound,sprites,gfx}
Copy only the downloadable content there — custom maps/*.bsp, their .res files, custom models/, sound/ and sprites/. Never copy config files, logs, or your .cfg secrets into a public web root.
3. Pre-compress with bzip2
The engine can fetch .bz2 versions of files, which cut transfer size dramatically. Compress every asset and keep the .bz2 alongside the original:
cd /var/www/fastdl/cstrike
find maps models sound sprites -type f ! -name "*.bz2" -exec bzip2 -kf {} \;
The -k flag keeps the source file. Clients request de_rats.bsp.bz2, download it, and decompress locally. Full detail is in FastDL bzip2 compression.
4. Write the Nginx server block
Create /etc/nginx/sites-available/fastdl:
server {
listen 80;
server_name dl.example.com;
root /var/www/fastdl;
location / {
autoindex off;
# serve .bz2 and .bsp as raw binary
types { }
default_type application/octet-stream;
add_header Cache-Control "public, max-age=604800";
}
# never expose config or list directories
location ~* \.(cfg|ini|log|res)$ {
deny all;
}
}
Setting default_type application/octet-stream matters: if Nginx returns a .bz2 with a wrong or text MIME type, some clients mishandle it. Enable the site and reload:
ln -s /etc/nginx/sites-available/fastdl /etc/nginx/sites-enabled/ nginx -t systemctl reload nginx
5. Point the server at the mirror
In server.cfg, set the download cvars. The URL points at the web root above the cstrike/ folder, because the engine adds cstrike/ itself:
sv_allowdownload 1 sv_downloadurl "http://dl.example.com"
The interaction between these cvars is explained in sv_allowdownload settings. Change the map after editing so the new value takes effect.
6. Keep the mirror in sync
Every time you add a map or model to the server, copy it to the mirror and re-run the bzip2 command, or clients 404 on the new content. Many admins automate this with a nightly rsync from the game's cstrike/maps into the web root followed by the compress step. For a global audience, front the host with a CDN as in FastDL behind Cloudflare.
7. Why HTTP beats the in-game transfer
It is worth understanding what you are actually fixing. The GoldSrc engine has its own file-transfer protocol layered over the game's UDP connection, and it is throttled hard — historically a handful of KB per second per client — because it was never meant to move megabyte-scale custom content. When ten players connect to a map they lack, the server spends its bandwidth dribbling files to them instead of running the game, and the round stalls. Moving those transfers to Nginx offloads them entirely: the game socket carries only gameplay, while a real web server — built to serve thousands of static files at once — handles the downloads at full line speed. That separation is the whole point of FastDL, and it is why a busy custom-content server is essentially unrunnable without it.
8. Serve it on the right host
The FastDL host does not have to be the game box, and often should not be. A separate small web server, or a static bucket behind a CDN, keeps download traffic off the machine running HLDS so a rush of connecting players never competes with the tick loop for CPU or bandwidth. If you do co-host, at least bind Nginx to a sensible worker count and let the OS cache the hot files; the assets are immutable, so aggressive caching is safe and the Cache-Control header above tells intermediaries so.
Troubleshooting
- Every file 404s — the
cstrike/level is missing or doubled in the web root. Open the exact URL a client requests, e.g.http://dl.example.com/cstrike/maps/de_dust2.bsp, in a browser. See FastDL not working. - The
.bz2404s but the raw file works — you did not compress that file, or only kept the.bz2for some. See the 404 on .bz2 fix. - Downloads still crawl —
sv_downloadurlis empty or unreachable, so the server fell back to the UDP channel. Test the URL in a browser from another network. - Config files exposed — you copied
.cfginto the web root and omitted thedenyblock. Remove them and keep the deny rule.
Verification
Delete a custom map from a test client, connect to the server, and watch the download bar — it should complete in seconds over HTTP rather than crawling. Cross-check the Nginx access log: you should see 200 responses for /cstrike/maps/<map>.bsp.bz2. A 404 there is a sync or path problem; a slow in-game transfer means the client never reached the mirror at all. The complete conceptual model is in the FastDL setup guide.









