Mantis trigger URLs are intentionally public. Put coarse limits at the first public edge so abusive traffic is dropped before it reaches Node, Postgres, or the notification queue. Use these as starting values, then adjust for your traffic: Notes:
  • /c/:public_id only needs a short public ID and an optional short src query parameter. A 2 KB URI ceiling is generous.
  • Mantis does not need request bodies on /c/*; POST is accepted only for clients that can only signal by POST.
  • If you intentionally embed long attribution in query strings, raise the URL limit for /c/* and keep the app’s storage caps in sync.
  • Node’s default HTTP request-header limit is 16 KiB. Railway’s public proxy allows 32 KB combined headers. The app stores request fields with caps above the Node default so normal accepted traffic is preserved.
  • Mantis also enforces app-level body caps where it parses request bodies: management JSON bodies are capped at 64 KiB, Apple Wallet log bodies at 32 KiB, and the dev inbox capture at 1 MiB. Oversized parsed bodies return 413 payload_too_large.
  • Stored hit headers are allowlisted and capped. Credential-shaped names such as authorization, cookies, session tokens, CSRF tokens, and API-key-looking headers are dropped before storage; x-mantis-* installer headers are kept.

Cloudflare Tunnel or Cloudflare in front of any host

Cloudflare is the cleanest place to enforce app-layer limits for public Mantis URLs. Rules only apply when the DNS record is proxied through Cloudflare.

Custom rule: reject oversized public URLs

Dashboard path:
  1. Cloudflare dashboard -> your zone -> Security -> WAF -> Custom rules.
  2. Create a rule named mantis public URL limits.
  3. Expression:
  1. Action: Block.
  2. Deploy.
If you changed MANTIS_PUBLIC_PATH, replace /c/ with that path.

Rate limiting rules

Dashboard path:
  1. Security -> WAF -> Rate limiting rules.
  2. Create one rule for /c/*:
    • Expression: starts_with(http.request.uri.path, "/c/")
    • Characteristics: IP address
    • Period: 60 seconds
    • Requests: 120
    • Mitigation timeout: 60 seconds
    • Action: Block or Managed Challenge
  3. Create one rule for /status/*:
    • Expression: starts_with(http.request.uri.path, "/status/")
    • Characteristics: IP address
    • Period: 60 seconds
    • Requests: 240
    • Mitigation timeout: 60 seconds
    • Action: Block
  4. If Apple Wallet is enabled, create one rule for /api/wallet/*:
    • Expression: starts_with(http.request.uri.path, "/api/wallet/")
    • Characteristics: IP address
    • Period: 60 seconds
    • Requests: 120
    • Mitigation timeout: 60 seconds
    • Action: Block
Request body-size matching with http.request.body.size is an Enterprise feature. On Free/Pro/Business plans, enforce body size in an origin proxy such as nginx/Caddy or rely on Mantis not reading /c/* bodies. Cloudflare references:

Railway

Railway has useful network limits, but not an app-layer WAF. Railway documents a 32 KB combined header limit, about 11,000 requests/sec per domain, and L4 DDoS mitigation; it explicitly recommends Cloudflare when you need WAF functionality. Recommended Railway setup:
  1. Deploy Mantis on Railway as usual.
  2. Add a custom domain in Railway.
  3. Put that hostname in Cloudflare DNS with the orange cloud enabled.
  4. Add the Cloudflare custom and rate limiting rules above.
  5. Set TRUST_PROXY_HEADERS=1 only when Cloudflare is the public entry point.
Without Cloudflare, keep MANTIS_DUPLICATE_LOG_LIMIT low and use hit retention so known URLs cannot grow the database indefinitely. Railway references:

Railway Redis/Valkey for shared rate limiting

Mantis does not require Redis/Valkey by default. Adding it on Railway means running another always-on service. Based on Railway’s current usage pricing:
  • RAM is billed at $10/GB-month.
  • CPU is billed at $20/vCPU-month by actual usage.
  • Volume storage is $0.15/GB-month.
  • Hobby has a $5/month minimum that counts toward usage.
A small Redis/Valkey limiter using 256 MB of RAM costs about $2.50/month in RAM before CPU and storage. If your Mantis app plus Postgres are still under the Hobby included usage, it may not change the bill; otherwise it adds a few dollars per month. Performance is usually fine for a limiter, but every /c/* hit adds a private-network round trip before the DB insert. Prefer Cloudflare or provider edge limits first, then add Redis/Valkey only if you run multiple Mantis replicas or need strict shared counters.

Fly.io

Fly’s http_service.concurrency protects each Machine from too many concurrent requests, but it is not a per-IP rate limiter or WAF. The example deploy/fly.toml.example already uses request-based concurrency. Recommended Fly setup:
  1. Keep type = "requests" with a hard limit your VM can actually handle.
  2. Use a Cloudflare-proxied custom domain for the public hostname if the Mantis URL will be exposed broadly.
  3. Add the Cloudflare rules above.
Fly reference:

Render

Render provides DDoS protection automatically, but application-layer abuse is still your responsibility. Use a Cloudflare-proxied custom domain if you need per-path URL and rate limits before traffic reaches the service. Render reference:

Tailscale Funnel

Tailscale Funnel is convenient, but it is not a full WAF. Use split mode from tailscale.md so only public routes are exposed, and keep dashboard/API traffic on the private Serve hostname. For stricter public limits with Tailscale:
  1. Prefer Cloudflare Tunnel if you own a domain and want edge rules.
  2. Or put nginx/Caddy between the Funnel sidecar and Mantis, then point Tailscale Serve/Funnel at that local proxy instead of mantis:3000.
Minimal nginx location shape:
You would also need a matching limit_req_zone in nginx’s http block. This is intentionally not in the default compose file because it adds another moving part for the common personal setup.

Local Docker

The compose file binds Mantis to 127.0.0.1 by default. That is the best local limit: no public edge exists. If you set MANTIS_BIND_HOST=0.0.0.0, put a local reverse proxy in front before exposing it to untrusted networks.