Cloudflare in Front of an ECS App: WAF, Cache Rules, and Tunnels for the Full-Stack Stack
Deploy Cloudflare's WAF, cache rules, and tunnels in front of ECS workloads. Production patterns for session handling, cache invalidation, and private routing—without the enterprise overhead.
Cloudflare in Front of an ECS App: WAF, Cache Rules, and Tunnels
Running ECS workloads behind Cloudflare looks straightforward until you're debugging session handling, cache invalidation, or privately-routed tasks somehow reachable by direct IP. This covers three essential layers: WAF rules, cache configuration, and Cloudflare Tunnels — with enough specificity to be useful and enough candour to flag where complexity lurks.
Why Cloudflare Over Native AWS Tools?
AWS offers ALBs, WAFv2, and CloudFront. They're solid. Cloudflare's edge network, unified rule syntax, and free-tier generosity make it genuinely competitive for most product teams — especially if you're shipping a Next.js or Astro frontend alongside a FastAPI or Django API on ECS Fargate. Managing one control plane for WAF, caching, DDoS, and DNS is operationally worthwhile.
Architecture Overview
Two ingress patterns exist. The first is conventional: your ALB is public, Cloudflare proxies in front. The second uses Cloudflare Tunnel (cloudflared), where your ECS task dials out to Cloudflare's network and there's no public-facing load balancer. The tunnel approach eliminates public ingress entirely; the ALB approach is operationally simpler if you already have load-balancing infrastructure. Both are covered below.
1. WAF Rules: What to Configure
Cloudflare's WAF includes managed rulesets (OWASP + proprietary) plus custom rules. Here's what matters for full-stack deployments:
Lock Down the Origin
If you're using the public ALB pattern, your ALB is directly reachable. Anyone finding your ALB DNS name bypasses Cloudflare entirely — including your WAF. Fix this:
- In your ECS security group, restrict inbound 443/80 to Cloudflare's published IP ranges.
- Automate this in Terraform or CDK, not manually.
Essential Custom Rules
Beyond the managed ruleset:
- Block non-Cloudflare traffic to API paths — use
ip.src not in $cloudflare_ipswith actionBlockon/api/*. - Rate-limit auth endpoints — target
/auth/tokenor/accounts/login/specifically. Credential stuffing is worth preventing. - Block suspicious bots by JA3 fingerprint or
cf.bot_management.score— requires Business plan or above.
Known Issue: OWASP Ruleset and Large Payloads
Cloudflare's managed OWASP ruleset may block legitimate form submissions with large payloads (e.g., Django admin uploads, rich-text editor posts). This appears in WAF analytics as a rule trigger, not an origin error. Check WAF Event Logs in your first week, especially around file uploads and form-heavy admin interfaces. If blocked, add a bypass rule scoped to your own IP or adjust the sensitivity of the triggering rule rather than disabling the entire ruleset.
2. Cache Rules: Avoiding Common Mistakes
Cloudflare caches by default based on file extension and Cache-Control headers. Mixed Next.js + API backends will suffer with defaults.
Next.js Static Assets
Next.js serves content-hashed assets under /_next/static/. Cache aggressively:
API Routes and Dynamic Pages
Bypass cache for all API traffic:
For ISR pages or Django views with Cache-Control: s-maxage, let your app control the TTL. Set Cloudflare to Respect Origin rather than overriding. The common mistake is setting an aggressive Cloudflare TTL that outlives your origin — users see stale data and you spend an hour debugging before realising the purge API exists.
Cache Key and Session Handling
If your app serves different content based on cookies (auth, locale, A/B tests), Cloudflare's default cache key doesn't distinguish by cookie — all users see the same cached response. Bypass cache when a session cookie is present:
Alternatively, configure cache key rules to include specific cookies, but bypassing is cleaner for session-aware content.
3. Cloudflare Tunnels with ECS Fargate
This pattern eliminates public ingress entirely. cloudflared runs as a sidecar, dials out to Cloudflare, and Cloudflare routes traffic inward through that persistent connection.
Running cloudflared in Fargate
Add a cloudflared container to your ECS task definition:
Store your tunnel token in AWS Secrets Manager, referenced via secrets in the task definition — never bake it into the image or pass it as plaintext.
Create the tunnel token via:
In Cloudflare Zero Trust → Networks → Tunnels, configure the public hostname to point to http://localhost:8000 (or your app's internal port).
When to Use Tunnels
Tunnels are operationally simpler for internal tools, admin interfaces, and APIs that don't need ALB features like sticky sessions or multi-target failover. You eliminate the public load balancer, certificate management, and security group ingress rules. Your ECS tasks have no inbound rules — the tunnel connection is outbound only. Trade-off: tunnel throughput is bound by the outbound bandwidth of your Fargate task's ENI, and you lose per-request observability at the load balancer layer. For public-facing APIs at scale, an ALB in front of Cloudflare is still the safer choice.
Summary
Combining locked WAF rules, sensible cache rules that respect your app's Cache-Control headers, and a Tunnel for private ingress covers most production ECS deployments. None of this is exotic, but defaults will fail quietly.
Get the security group lockdown right first. Cache rules second. Tunnels third — optional but worthwhile for internal workloads or to eliminate public ingress from your threat surface.
Damian Hodgkiss
Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.