June 24, 2026
We Built a Self-Hosted WAF You Can Deploy in 10 Minutes
Stop Paying Per-Request for a WAF: Self-Host Caddy + Coraza + OWASP CRS in 10 Minutes

By SOCFortress
4 min read
Stop Paying Per-Request for a WAF: Self-Host Caddy + Coraza + OWASP CRS in 10 Minutes
Automatic TLS, the OWASP Core Rule Set, and a real management UI โ open-source and self-hosted.
Putting a Web Application Firewall in front of an app should be a one-afternoon job. In practice, it rarely is.
You wrestle with TLS certificates and their renewals. You bolt the OWASP Core Rule Set onto nginx or Caddy and immediately start fighting false positives โ tuned, if you're lucky, by hand-editing ModSecurity config in a syntax nobody enjoys. When something gets blocked, you dig through log files trying to work out which rule fired and why. And if you'd rather not deal with any of that, the cloud WAFs are happy to help โ for a per-request fee and a side of vendor lock-in.
We wanted the good parts without the tax. So we built a self-hosted WAF management platform you can stand up from prebuilt images in about ten minutes. Here's the full walkthrough:
What it actually is
Three pieces in one Docker Compose stack:
- Caddy as the reverse proxy โ and, crucially, automatic HTTPS.
- Coraza, an open-source, ModSecurity-compatible engine, running the OWASP Core Rule Set v4.
- A FastAPI + React management UI so you manage sites, rules, and exclusions by clicking โ not by editing config files.
It runs entirely on your infrastructure. No per-request billing. No traffic leaving your network. No lock-in.
Internet
โ HTTPS (auto TLS)
โผ
Caddy + Coraza + OWASP CRS โโโบ your app (private, WAF-only)
โ blocks attacks ยท logs everything
โผ
FastAPI API โโโบ PostgreSQL / Redis โโโบ React UI Internet
โ HTTPS (auto TLS)
โผ
Caddy + Coraza + OWASP CRS โโโบ your app (private, WAF-only)
โ blocks attacks ยท logs everything
โผ
FastAPI API โโโบ PostgreSQL / Redis โโโบ React UIYour protected app never gets a public address of its own. All traffic flows through the WAF.
Deploy in minutes
The images are published to GitHub Container Registry, so there's no source to build:
git clone https://github.com/socfortress/waf-platform-public.git
cd waf-platform-public
cp .env.example .env
# set a few secrets in .env (DB password, JWT key, admin password)
docker compose up -d
docker compose ps # everything healthy in under a minutegit clone https://github.com/socfortress/waf-platform-public.git
cd waf-platform-public
cp .env.example .env
# set a few secrets in .env (DB password, JWT key, admin password)
docker compose up -d
docker compose ps # everything healthy in under a minuteLog into the UI on port 8443, change the bootstrap admin password, and you're ready to add your first site.
Certificates, handled
This is the part most setups get wrong. When you add a site, you pick a cert mode. Choose Let's Encrypt, give it a hostname, and Caddy provisions and renews the certificate automatically โ no certbot, no PEM files, no renewal cron. The ACME challenge is even allowed to bypass the WAF automatically, so issuance never gets caught in your own rules.
Prefer to bring your own? Manual and self-signed certs are supported too. But for most people, "set hostname, pick Let's Encrypt, done" is the entire TLS story.
Blocking real attacks
To make this tangible, the demo puts a small app โ a SOC analyst "Threat Library" โ behind the WAF, plus an Attack Console that fires real payloads at it. Because every request flows through the WAF first, you watch it work in real time:
- SQL injection โ 403 Blocked
- Cross-site scripting โ 403 Blocked
- Path traversal, command injection, remote file include โ 403 Blocked
- Normal user traffic โ 200 OK
That last point matters as much as the blocks. A WAF that blocks everything is easy. A WAF that blocks attacks while real users sail through is the actual goal.
See exactly what's blocked โ and why
Every block is logged in real time with the detail you need to act on it: the triggered rule ID, the attack category, the client IP, and the GeoIP country. There's a live stream, search and filter, and a dashboard with your top rules, top source countries, and block rate.
No more grepping audit logs to reverse-engineer a 403.
The real test: tuning a false positive without opening a hole
Here's where most WAF rollouts fall apart. The CRS is aggressive by design, and sooner or later it blocks something legitimate. The lazy fix โ disable the rule globally โ punches a hole in your protection. The platform makes the right fix easy.
In the demo, a SOC analyst searches the knowledge base for a PowerShell indicator: powershell -enc base64 payload. Perfectly legitimate for an analyst โ but it trips a CRS rule and gets blocked. The fix takes two clicks:
- Open Logs, find the blocked request, and read the exact rule that fired โ here,
932120(Windows PowerShell command detection). - Go to Rules โ CRS Rules, find
932120, and add an exclusion scoped to just the search parameter (ARGS:term).
Now the analyst's search works. But โ and this is the whole point โ the same payload sent as an actual attack to a different endpoint is still blocked, and every other rule is untouched. One false positive cleared, zero holes opened. No disabling rules. No editing config files.
Roll it out safely
Not ready to block on day one? Flip any site to detection-only mode. Requests aren't blocked, but everything that would have been is logged. Deploy in front of production, watch for a week, tune your exclusions against real traffic, and switch to blocking once you're confident.
A note on how it's distributed
The platform is source-available: you run it from public, prebuilt images, and everything you need to deploy and operate it โ the Compose file, docs, and the full demo โ lives in the public repo. Internal development happens privately; you get a clean, supported artifact to run.
Get started
Everything in the video, including the demo app and the Attack Console, is in the repo:
๐ https://github.com/socfortress/waf-platform-public
git clone https://github.com/socfortress/waf-platform-public.git
cd waf-platform-public && cp .env.example .env
docker compose up -dgit clone https://github.com/socfortress/waf-platform-public.git
cd waf-platform-public && cp .env.example .env
docker compose up -dSelf-hosted, automatic TLS, OWASP CRS, full visibility โ no per-request fees, no lock-in.
Need Help?
The functionality discussed in this post, and so much more, are available via the SOCFortress platform. Let SOCFortress help you and your team keep your infrastructure secure.
Website: https://www.socfortress.co/
Contact Us: https://www.socfortress.co/contact_form.html