DH
5 min read

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

User → Cloudflare Edge (WAF + Cache) → ALB → ECS Fargate Tasks

(or via Cloudflare Tunnel, no ALB needed)

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:

  1. In your ECS security group, restrict inbound 443/80 to Cloudflare's published IP ranges.
  2. Automate this in Terraform or CDK, not manually.
resource "aws_security_group_rule" "allow_cloudflare" {
for_each = toset(var.cloudflare_ipv4_ranges)
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [each.value]
security_group_id = aws_security_group.alb.id
}

Essential Custom Rules

Beyond the managed ruleset:

  • Block non-Cloudflare traffic to API paths — use ip.src not in $cloudflare_ips with action Block on /api/*.
  • Rate-limit auth endpoints — target /auth/token or /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:

URI Path: contains /_next/static/
Cache TTL: 1 year (Edge) + 1 year (Browser)
Cache Status: Override → Cache Everything

API Routes and Dynamic Pages

Bypass cache for all API traffic:

URI Path: starts with /api/
Cache Status: Bypass

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:

Cookie: contains sessionid → Bypass Cache
Cookie: contains __Secure-next-auth.session-token → Bypass Cache

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:

{
"name": "cloudflared",
"image": "cloudflare/cloudflared:latest",
"command": ["tunnel", "--no-autoupdate", "run", "--token", "YOUR_TUNNEL_TOKEN"],
"secrets": [
{
"name": "TUNNEL_TOKEN",
"valueFrom": "arn:aws:secretsmanager:eu-west-1:123456789:secret:cloudflare-tunnel-token"
}
]
}

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:

cloudflared tunnel create my-ecs-tunnel
cloudflared tunnel token my-ecs-tunnel

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

Damian Hodgkiss

Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.

Creating Freedom

Join me on the journey from engineer to solopreneur. Learn how to build profitable SaaS products while keeping your technical edge.

    Proven strategies

    Learn the counterintuitive ways to find and validate SaaS ideas

    Technical insights

    From choosing tech stacks to building your MVP efficiently

    Founder mindset

    Transform from engineer to entrepreneur with practical steps