
SafeLine WAF: a serious open-source firewall for your web apps
If you have ever stared at a 5xx graph at 3 AM while a botnet pokes every login form you own, you already know why a Web Application Firewall matters. The honest question is which one to put in front of your stack without paying Cloudflare Enterprise money or fighting ModSecurity rulesets for a weekend.
SafeLine , the open-source WAF maintained by Chaitin Tech , has been quietly building momentum around that exact gap. As of v9.3.4 (released 17 April 2026) it sits at 21k+ stars on GitHub, runs on more than 180,000 installations and is licensed under GPL-3.0. We have been testing it on a VPS in our Bucharest datacenter for a few weeks. This post is the field report.
What SafeLine actually is
Architecturally, SafeLine is a reverse proxy with a detection engine in front of your origin. Traffic hits SafeLine on ports 80/443, gets inspected, and either passes through to your upstream application or is challenged, throttled, or blocked. The reverse proxy is built on a hardened Tengine fork (Tengine being Alibaba’s nginx derivative), with detection logic implemented as native modules in C++ and Lua. The management plane is a Go service backed by PostgreSQL.
If that sounds a lot like nginx + ModSecurity, the comparison is fair on paper and unfair in practice. The interesting part is what happens between request arrival and the upstream call.
Why the detection engine is different
Most open-source WAFs you have probably touched, ModSecurity with the OWASP Core Rule Set being the canonical example, are fundamentally regex engines. They match patterns in the request and decide. The trade-off is well known: tighten the rules and you start blocking legitimate users, loosen them and attacks slip through. Anyone who has ever paranoia-mode’d ModSecurity in front of a WordPress site knows the false-positive tax.
SafeLine takes a different route. It parses the request as a syntactic structure (SQL grammar for query parameters that look database-shaped, JS AST for script content, shell parser for command patterns) and then evaluates whether the parsed result has the semantic shape of an attack. A query with quotes is not automatically suspicious; a query whose parsed form would change SQL execution flow is. The Chaitin team publishes benchmark numbers around 99.4% true-positive accuracy in their balance mode, which is in the range of commercial offerings.
You should still verify on your own traffic, all WAF benchmarks have a strong “tested on the dataset we built” flavour, but in practice the false-positive rate on our test workloads has been substantially lower than ModSecurity with CRS at paranoia level 2. That alone changes the operational story: a WAF you do not have to constantly whitelist around is a WAF that stays on.
What you get out of the box
The community edition (which is what we are talking about, the Pro edition is announced but not generally available at the time of writing) ships with:
- Web attack detection for the OWASP-canonical categories: SQL injection, XSS, command and code injection, XXE, SSRF, path traversal, deserialization patterns.
- Rate limiting per IP, per path, per session, with configurable windows and burst behaviour.
- Anti-bot challenge, a JavaScript proof-of-work that legitimate browsers solve transparently and headless scrapers stumble on.
- Authentication challenge, a separate gate you can put in front of admin panels (think: a captcha-like step before reaching
/wp-admin/). - Dynamic HTML/JS encryption, which mangles client-side identifiers per request so scrapers and credential-stuffing tools cannot easily target form fields.
- Geo blocking by country, useful for sites with no business outside a specific region.
- Logging and analytics dashboard, which is genuinely useful (more on this below).
What it does not do: it is not a CDN. It does not cache static assets, does not give you POPs around the world, does not protect against L3/L4 volumetric DDoS at the network edge. SafeLine is a layer-7 application firewall. If you are taking 100 Gbit of UDP reflection, you need network-layer mitigation upstream, the kind we run at AS57050 with our own RTBH and sFlow setup , before the traffic ever reaches your VPS.
Installing SafeLine on a VPS
This is the part where most “open source WAF” projects make you regret the decision. SafeLine genuinely does not. The official one-liner does the right thing on a clean Debian or Ubuntu box.
Minimum sane resources: 2 vCPU, 4 GB RAM, 10 GB free disk. The script will warn under 5 GB free. SSSE3 CPU instructions are required (any x86 CPU made in the last 15 years has them, but if you are running on something exotic, check /proc/cpuinfo). Both x86_64 and arm64 are supported.
On a fresh server:
bash -c "$(curl -fsSLk https://waf.chaitin.com/release/latest/setup.sh)"
The script will:
- Install Docker and the Compose plugin if they are missing.
- Drop a docker-compose stack at
/data/safeline/. - Pick an unused private subnet (it tries
172.22.222.0/24,169.254.222.0/24,192.168.222.0/24in order). - Generate a random 32-char Postgres password.
- Pull and start four containers:
safeline-pg,safeline-mgt,safeline-detector,safeline-tengine.
Once it finishes, the management UI is at https://<your-server-ip>:9443/ with a self-signed certificate. To grab the initial admin credentials:
docker exec safeline-mgt resetadmin
That command prints a one-time username and password you can use for the first login. Change the password immediately after you are in. While you are at it, restrict port 9443 with a firewall rule, the management plane should not be exposed to the internet:
ufw allow from YOUR_OFFICE_IP to any port 9443
ufw deny 9443
If you are running this on a DreamServer VPS , the same rule belongs in your provider’s network firewall too, do not rely on host-level rules alone for management interfaces.
Putting a real site behind it
Adding a protected site is straightforward but the mental model takes a minute to click. SafeLine becomes the public-facing endpoint; your real application becomes an internal upstream that only SafeLine talks to.
In the management UI, Sites > Add site:
- Domain:
example.com(and any aliases you serve). - Port:
80for plain HTTP,443for HTTPS, both if you want SafeLine to do the redirect. - Upstream: the IP and port of your origin, for example
10.0.0.5:8080. If your origin lives on the same box, use the docker bridge gateway (typically172.17.0.1or whatever the script picked) and the port your application listens on. - TLS certificate: paste a cert and key, or upload a Let’s Encrypt-issued one. SafeLine does not currently auto-issue certificates, you bring your own.
Save, then point your domain’s A/AAAA records at the SafeLine host. Within a few seconds the site shows up under Detection logs and you can watch live traffic flow through.
The first thing to verify is that SafeLine is genuinely inline. Send a deliberately bad request:
curl -k "https://example.com/?id=1' OR '1'='1"
You should get a SafeLine block page (HTML body, HTTP 403 by default) and a corresponding entry in the detection log. If the request goes through to your application, your DNS or upstream config is wrong, fix that before you trust anything else.
Configuration patterns worth knowing
A few patterns we have found genuinely useful beyond the defaults.
Whitelisting health checks
If your monitoring system pings /health from a known IP, do not let SafeLine score those requests. Add an Allow rule at the top of the policy chain matching source IP and path, with action “skip detection”. The point is not to save CPU, it is to keep your detection logs uncluttered with traffic you trust.
Rate limit before block
When you turn on rate limiting for a login endpoint, prefer challenge over block for the first threshold. A bot that runs into a challenge silently dies; a human who fat-fingered their password three times in a row gets through. Reserve the block action for the second threshold (say, 30 requests per 5 minutes from a single IP).
Anti-bot for the entire site, not just login
Counter-intuitive but useful: turn on the JS challenge sitewide for low-value paths (the homepage, the blog) on a small percentage of traffic. The bots that fail the challenge there are the same ones that would later try your login form. You learn a lot about what is hitting you before any sensitive endpoint sees the traffic.
Authentication challenge for admin paths
For anything matching /admin*, /wp-admin*, /phpmyadmin*, add an authentication challenge layer. SafeLine implements this as an extra gate that requires solving a CAPTCHA-style challenge before the request reaches your application. It does not replace your application’s auth, it just stops automated tools from ever knocking on the door.
Read the logs
The dashboard at Statistics > Detection logs shows every blocked request with the parsed reason: which engine triggered (semantic SQLi, regex XSS, anti-bot), the request body, the score. Spend an hour going through it the day after you turn SafeLine on. You will find that most of your “weird traffic” looks the same: a handful of mass-scanning toolkits hitting the same WordPress admin paths and the same exposed .env URLs.
Where SafeLine is not the right answer
It is worth being honest about the limits.
- Not a CDN. No caching, no edge POPs. If your performance story depends on edge proximity, you still need a CDN in front of (or beside) SafeLine.
- Not for sub-millisecond paths. Every request goes through detection. Median overhead in our tests was 1 to 3 ms on warm caches, which is fine for almost any web app, but if you are running an HFT API, this is not your tool.
- Pro features are not all open. Some of the more sophisticated dynamic-protection features and the multi-node clustering are clearly aimed at the upcoming Pro edition. The community edition is generous, but if you are protecting a fleet of dozens of sites with HA requirements, plan for that.
- Self-hosting is not free. You are running another stateful service that needs backups, OS patching, and the occasional Postgres babysitting. If your team does not want that, a managed WAF will be cheaper in operator hours.
Alternatives we considered
For completeness: we also looked at BunkerWeb
(nginx-based, very approachable, weaker semantic detection), Coraza
(a Go-native ModSecurity reimplementation, great if you want to embed WAF logic directly in a service mesh), and the OWASP CRS on a stock nginx with ngx_security_module (battle-tested, infinitely tunable, false positives are your hobby now).
SafeLine ended up being the cleanest fit for “I want a real WAF in front of a small fleet of self-hosted sites without becoming a WAF specialist”. Your weights might be different.
So, should you run it?
If you self-host PHP, Node, Python or Go web applications on a VPS or dedicated server and you have been meaning to “do something about security headers and bot traffic”, SafeLine is worth a Sunday afternoon. The install is genuinely fast, the detection works without you fighting it, and the dashboard tells you something true about your traffic.
If you are running it in production, put it on a server with a real backup story (Proxmox Backup, snapshots, your call) and treat the management plane as critical infrastructure. The detection containers can be rebuilt in a minute; the configuration in Postgres is what you would actually miss.
We run SafeLine ourselves in front of a couple of internal services and recommend it to customers who ask. If you want to try it on infrastructure you do not have to build first, our VPS plans start at small enough sizes to make the experiment cheap, and our server administration service covers the install and tuning if you would rather skip the learning curve.
Either way, kicking the tires on SafeLine is a better way to spend an evening than reading another “top 10 WAF” listicle. The code is on GitHub, the install is one command, and you will know within an hour whether it earns a spot in your stack.