Web Application and API Protection on Linux
Why WAAP and NGWAF matter for self‑hosters
When you expose a service over HTTP(S), the attack surface expands beyond the kernel. Modern web apps and APIs get hit with injection, XSS, CSRF, and the newer API‑specific threats like replay or rate‑limit abuse. A WAAP (Web Application and API Protection) or NGWAF (Next‑Generation Web Application Firewall) sits in front of your HTTP stack and can stop most of those before they even reach your code.
If you’re running a reverse proxy, containers, or a bare‑metal service, you can get a lightweight NGWAF that plays nicely with systemd, Docker, Nginx, Caddy, etc. – no need for a big appliance that sits on a separate machine.
Choosing a Linux‑friendly NGWAF
| Tool | Deployment | Key Features | Trade‑offs |
|---|---|---|---|
| ModSecurity (via Nginx or Apache) | Module or standalone | OWASP Core Rule Set (CRS), rule language, detailed logging | Requires module build, can be CPU hungry |
| Caddy + caddy‑waf | Built‑in | Automatic HTTPS, simple rule syntax, low memory | Smaller rule set than ModSecurity |
| Nginx‑Plus WAF | Commercial | Advanced rule engine, JSON API, auto‑updates | Paid, needs Nginx‑Plus |
| OpenResty + LuaWAF | Lua scripting | Full control, can embed ML models | Requires Lua knowledge |
| Traefik + Traefik‑WAF | Reverse proxy | Declarative rules, Docker/K8s integration | Limited rule language, still maturing |
| Cloudflare WARP | Cloud‑based | Edge protection, no local install | Relies on external provider |
For a homelab or a small team, ModSecurity with the CRS is a solid default. It runs as a module in Nginx or Apache, has a huge rule base, and can be tuned to keep false positives at bay.
Installing ModSecurity on Debian/Ubuntu
sudo apt update
sudo apt install -y libnginx-mod-security nginx
Enable the module in /etc/nginx/nginx.conf:
http {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
...
}
Download the latest CRS:
cd /etc/nginx/modsec
sudo wget https://github.com/coreruleset/coreruleset/archive/refs/heads/master.zip
sudo unzip master.zip
sudo mv coreruleset-master/* .
sudo rm -rf coreruleset-master master.zip
Activate the rules:
SecRuleEngine On
Include /etc/nginx/modsec/crs-setup.conf
Include /etc/nginx/modsec/rules/*.conf
Restart Nginx:
sudo systemctl restart nginx
Tuning for performance
The CRS is conservative; it can churn out a lot of logs and, on busy sites, become a bottleneck. Two quick fixes:
Drop the rules you don’t need – start with the “recommended” set and peel off the ones that trigger false positives.
# In /etc/nginx/modsec/crs-setup.conf SecRuleRemoveById 960015 # Example: disable rule 960015Use the “phase 2” rule set only – phase 1 checks request headers; phase 2 checks the body. If you only care about header protection, skip phase 2.
SecRuleEngine DetectionOnly
Check that the module is compiled with --with-modsecurity via nginx -V. Then watch CPU with htop or perf.
Integrating with systemd
If you’re running Nginx in a container or a systemd‑managed service, add a watchdog to restart the service when the WAF blocks an attack:
# /etc/systemd/system/nginx.service.d/watchdog.conf
[Service]
WatchdogSec=30
ExecStartPost=/usr/bin/echo "WAF triggered, restarting Nginx" >> /var/log/nginx/waf.log
Reload systemd:
sudo systemctl daemon-reload
sudo systemctl restart nginx
API‑specific protection
APIs usually talk JSON. ModSecurity can parse JSON with the ModSecurity JSON module. Enable it:
sudo apt install -y libmodsecurity-mod-json
Add a rule to block malformed JSON:
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"chain,phase:2,pass,log,deny,status:400,msg:'Invalid JSON'"
SecRule REQUEST_BODY "@validateJson" "t:none"
For rate limiting, use SecAction:
SecAction "id:900000,phase:1,nolog,pass,ctl:ruleEngine=On, \
setvar:tx.rate_limit=100, \
setvar:tx.rate_window=60, \
setvar:tx.rate_action=deny"
That caps each IP to 100 requests per minute.
Comparing to a cloud WAF
Cloud providers (AWS WAF, Cloudflare, Azure Front Door) give you a managed WAAP with automatic rule updates and global DDoS protection. For a small self‑hosted site, the extra cost and edge latency can outweigh the upside. If you’re already using a CDN, a cloud WAF can be a zero‑maintenance add‑on, but for most home labs a local NGWAF is cheaper and more controllable.
Common pitfalls and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 502 Bad Gateway after rule update | Rule syntax error | Run modsecurity-check or nginx -t |
| Legitimate requests blocked | Overly aggressive rule | Disable rule by ID or tweak thresholds |
| High CPU usage | Large rule set | Reduce the set, enable SecRuleEngine DetectionOnly |
| No logs | Logging disabled | Make sure SecAuditLog is set in crs-setup.conf |
Parse audit logs with modsecurity-log:
sudo modsecurity-log /var/log/nginx/modsec_audit.log
Hardening the WAF itself
Run the WAF in a separate container – isolate it from the app.
podman run -d --name waf -p 8080:80 nginx:alpineRestrict the rule set – enable only what your stack actually needs.
Keep the CRS up to date – a cron job can pull the latest rules.
0 3 * * * root wget -O /etc/nginx/modsec/crs-setup.conf https://raw.githubusercontent.com/coreruleset/coreruleset/master/crs-setup.confEnable TLS termination – let the WAF handle HTTPS to reduce SSL/TLS overhead on the backend. Use Caddy or Nginx with
ssl_certificatedirectives.
When to move beyond ModSecurity
- High traffic – a dedicated NGWAF appliance or a cloud edge may be worth it.
- Complex rule logic – OpenResty (Lua) or a policy engine like OPA gives you more control.
- ML‑based detection – some vendors provide anomaly detection that learns traffic patterns; those are usually commercial.
Quick checklist for a production‑ready NGWAF
- Nginx/Apache with ModSecurity module enabled.
- CRS installed and tuned.
- API JSON validation rules added.
- Rate limiting configured per IP.
- Audit logs rotated (
logrotate). - Systemd watchdog or health check in place.
- Regular rule updates via cron.
- Performance baseline measured (CPU, latency).
Adding a WAAP on Linux is more about smart configuration than buying a fancy appliance. With ModSecurity and a few tweaks you can erect a solid first line of defense against the most common web and API attacks while staying in full control of your stack.
See also
- How to Use systemd‑resolved to Serve a Local Search Domain on My Home Network
- Pulling a Single File from a Borg Backup Without Unpacking the Whole Archive
- Stop systemd‑journald from filling your Raspberry Pi’s 8 GB SD card with logs – how to cap its size
- Send a Telegram Alert When Your Home Server’s /home Reaches 90% Usage With a Tiny systemd Service and curl
- Stop the “REMOTE HOST IDENTIFICATION HAS CHANGED!” error by cleaning ~/.ssh/known_hosts.