DEV Community

Cover image for Caching API Responses at the Edge with Cloudflare Cache Rules
Anand Rathnas
Anand Rathnas

Posted on

Caching API Responses at the Edge with Cloudflare Cache Rules

Your API is getting hammered. Same endpoints, same responses, thousands of requests. Your origin server is sweating. Here's how I used Cloudflare Cache Rules to serve API responses from the edge—no code changes required.

The Problem

I run a URL shortener. The redirect API (/api/v1/public/a/{shortUrl}) returns JSON with the destination URL, and the frontend handles the redirect. Popular links get hit thousands of times a day.

The thing is—the response rarely changes. A short URL pointing to https://example.com will point there for days, weeks, months. Why hit the origin every single time?

Why Not Just Use Cache-Control Headers?

You could add Cache-Control: public, max-age=3600 in your backend code. But:

  1. Code changes = deployments, testing, potential bugs
  2. Cloudflare doesn't cache API responses by default (even with headers)
  3. Less flexibility—TTLs are baked into code

Cache Rules let you handle this entirely at the infrastructure layer.

The Setup

Cloudflare Dashboard → Caching → Cache Rules → Create Rule

The Expression

(http.request.uri.path contains "/api/v1/public/a/" and not any(http.request.headers["jo4_url_pwd"][*] ne ""))
Enter fullscreen mode Exit fullscreen mode

This matches:

  • Requests to the short URL API
  • AND excludes requests with a password header (for protected URLs)

Why exclude password-protected URLs? If you cache them, anyone hitting that URL gets the cached response—including the destination. Security hole.

The Cache Settings

Setting Value Why
Cache eligibility Eligible for cache Enable caching for matching requests
Edge TTL 2 hours How long Cloudflare edge holds it
Browser TTL 1 hour How long the client browser holds it

Why Staggered TTLs?

The 1hr browser + 2hr edge combo is intentional:

  • 0-1hr: Browser serves from local cache (instant, zero network)
  • 1-2hr: Browser cache expired, but Cloudflare edge still has it (fast, no origin)
  • 2hr+: Both expired, request hits origin (fresh data)

This maximizes cache hits while keeping staleness reasonable.

What About Analytics?

Here's the tradeoff: if Cloudflare serves a cached response, your origin never sees the request. No analytics recorded.

For my use case, this is fine. I care about unique visitors, not repeat hits from the same client. The first request hits origin (analytics recorded), subsequent requests get cached.

If you need accurate hit counts:

  1. Use a shorter TTL (15-30 minutes)
  2. Implement client-side analytics beacons
  3. Or just don't cache—sometimes origin load is acceptable

The Visual Builder

If you prefer clicking over typing expressions:

When incoming requests match...

  • Field: URI Path | Operator: contains | Value: /api/v1/public/a/
  • And
  • Field: Request Header | Name: jo4_url_pwd | Operator: equals | Value: (empty)

Actually, for "header not present", the expression editor is easier. The visual builder's equals empty works but the expression not any(http.request.headers["header"][*] ne "") is cleaner.

Cache Invalidation

What if a URL changes? Two options:

  1. Wait for TTL (2 hours max in my case—acceptable)
  2. Purge via API:
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://yourdomain.com/api/v1/public/a/abc123"]}'
Enter fullscreen mode Exit fullscreen mode

For most use cases, just pick a TTL you can live with and skip the invalidation complexity.

Bonus: Serve Stale While Revalidating

Enable "Serve stale content while revalidating" in your cache rule. When cache expires:

  1. Cloudflare immediately serves the stale cached response
  2. Simultaneously fetches fresh content from origin
  3. Next request gets the fresh version

Users never wait for origin. Cache stays warm.

Results

Before: Every request hit origin. Popular URLs caused load spikes.

After: ~90% of repeat requests served from edge. Origin load dropped significantly. Response times for cached requests: <50ms globally (served from nearest Cloudflare POP).

Lessons Learned

  1. Cloudflare doesn't cache API responses by default—you need Cache Rules to enable it
  2. Exclude sensitive requests—use expressions to skip password-protected or authenticated endpoints
  3. Stagger your TTLs—browser TTL < edge TTL gives you layered caching
  4. Accept the analytics tradeoff—or implement client-side tracking
  5. Skip invalidation if you can—pick a TTL you can live with, it's simpler
  6. Zero code changes—this is purely infrastructure config

Have questions about edge caching? Drop a comment below.

Building jo4.io - URL shortener with analytics, bio pages, and team workspaces.

Top comments (0)