CfBouncer - Automatically sync Cloudflare WAF rules from your Phoenix routes

Hey everyone! I just released CfBouncer, a small library that keeps your Cloudflare WAF block rules in sync with your Phoenix application.

The problem

If you’re using Cloudflare in front of your Phoenix app, you might want to block requests to paths that don’t exist in your application — vulnerability scanners probing for /wp-admin, /.env, /phpmyadmin, etc. Manually maintaining a WAF rule for this gets tedious and falls out of date as your app evolves.

What CfBouncer does

CfBouncer reads your Phoenix router, endpoint websockets, and static file paths, then generates a Cloudflare WAF expression that blocks everything except your known routes. It pushes this rule via the Cloudflare API.

For example, given routes like /, /users/:id, /auth/login and static paths like assets, robots.txt, it generates:

not (
  http.request.uri.path eq "/"
  or starts_with(http.request.uri.path, "/assets/")
  or starts_with(http.request.uri.path, "/auth")
  or starts_with(http.request.uri.path, "/live")
  or starts_with(http.request.uri.path, "/robots.txt")
  or starts_with(http.request.uri.path, "/users")
)

Usage

Add it to your mix deps:

{:cf_bouncer, "~> 0.1.0"}

Configure your router, endpoint, and Cloudflare credentials:

# config/config.exs
config :cf_bouncer,
  router: MyAppWeb.Router,
  endpoint: MyAppWeb.Endpoint,
  static_module: MyAppWeb,
  rule_description: "[CfBouncer] Block non-allowlisted paths"

# config/runtime.exs
config :cf_bouncer,
  zone_id: System.get_env("CLOUDFLARE_ZONE_ID"),
  api_token: System.get_env("CLOUDFLARE_API_TOKEN")

Then run it as part of your deploy:

mix cf_bouncer.sync

It’s idempotent — it only updates the rule when the expression actually changes. There’s also --dry-run to preview what would be pushed and --force to push regardless.

You can also add extra_paths for routes served outside Phoenix (proxied services, webhooks, etc.).

Cloudflare

On Cloudflare - your pushed rule will look like this:

Links

Would love to hear your feedback!

3 Likes

Congrats on the release, this is a very cool library.

I have three suggestions.

  • Don’t bring in Req by default. Use the built in HTTP library - httpc. Maybe Req can be optional dependency.
  • For the text generation part you can use EEx instead of concatenation strings.
  • You use the application environment while your library is not a supervised application. It’s not a very good practice. The docs feature library guidelines.

Thanks for the tips. Just pushed the new version:

## 0.2.0

- Switch from `Req` to Erlang's built-in `:httpc` for HTTP requests
- Use EEx templates for configuration
- Only read application config in mix task, not in library code
2 Likes