◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-10-20Intermediate

My PHP API Returned no-store and Cloudflare Passed Everything Through: Overriding Only the Edge TTL

The list API behind my wallpaper apps returned no-store, so putting Cloudflare in front of it changed nothing: every request still reached the origin. This note covers a Cache Rule that overrides only the edge TTL, a small script that proves it works, and a deploy step that pairs upload and purge so stale responses don't linger.

Cloudflare5Cache RulesPHPAPI designcaching3indie dev23Antigravity392

✦ Premium Article

I had known for a while that the list API behind my wallpaper apps was heavier than it should be. Each category list is long, and every time the app opens, a PHP script on the origin rebuilds the same content and sends it back.

I had already put Cloudflare in front of image delivery, so I assumed the API was covered too. It wasn't. cf-cache-status never showed HIT, and fetching the same list repeatedly went to the origin every time. The CDN was sitting there without ever acting as a cache.

The culprit was a plain Cache-Control: no-store coming from PHP. This is a common state for an indie developer's API, so here is the fix, written under one constraint: do not change anything on the device side.

Two separate reasons caused the pass-through

First I wanted to know why HIT never appeared. There were two reasons stacked on each other.

The first: by default Cloudflare caches static file extensions such as images and CSS, and JSON returned from a .php endpoint is not among them. The second: the origin declared no-store. When a session starts, PHP adds Cache-Control: no-store, no-cache, must-revalidate by default. Even an API that never uses sessions gets the same header if a shared include happens to call session_start().

So both the extension and the header pushed the response toward "the edge keeps nothing". Fixing only one of them is not enough.

Three options, chosen by whether they reach the device

I considered three approaches.

ApproachEdgeDevice (app)My call
Change origin Cache-Control to public, max-age=…WorksWorks (stale responses also stick on devices)Rejected
Override only Edge TTL with a Cache RuleWorksUnchangedChosen
Rewrite responses in a WorkerWorksDepends on the rewriteOverkill, rejected

What decided it was whether the change reaches the device. Shipped app versions keep running on devices of users who haven't updated. If I changed the origin's Cache-Control, those old versions would also be told "don't refetch for a while", and a response already stored on a device is something I can never remove from my side. Overriding only the edge TTL leaves the device's instructions as they were and increases only what the edge absorbs.

One more precondition: an API whose clients read fields by position can only append fields at the end. I wrote about keeping that promise in a separate article. It is worth confirming that promise still holds before adding any caching in front of such an API.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You can tell within minutes whether your own API is passing straight through a CDN, using a short script that lines up cf-cache-status and age across repeated requests
✦You will be able to choose the design that overrides only the edge TTL and leaves the origin's Cache-Control alone, so you avoid dragging already-shipped app versions into a device-side caching change before you ever apply it
✦You can close the stale-response window that caching opens by bundling upload and purge into one step, saving the time you'd otherwise spend answering 'I updated it but the app still shows the old list' reports
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-10-15
I Swapped an Immutable Asset at the Same URL, and Only the Browser Kept the Old Copy
Overwrite a long-cached JS file at the same URL and neither a CDN purge nor a refresh will make the browser fetch it again. Four delivery styles — same URL, no-cache, query string, content-hash filename — compared in Chromium, plus a complete build-step script that adds the hash.
▣ App Dev2026-09-29
Purging Cloudflare Didn't Bring Back the New Wallpapers: the Stale JPEGs Were Held by XPageSpeed on the Origin
An operations record from a wallpaper app: images overwritten at the same URL kept arriving stale even after a Cloudflare purge. Covers the three-way local/origin/edge probe that names the guilty layer, when prefix purge beats per-URL purge, and how to switch off Xserver's XPageSpeed in .htaccess without a 500.
▣ App Dev2026-05-06
Running a Solo AI Studio with Antigravity × Google AI — Automating Every Stage of App Development as One Developer
How to combine Antigravity 2.0 and Antigravity CLI with Stitch and Veo 3 into a pipeline that takes an app from design through implementation, testing, asset generation, and App Store submission, driven by one developer. Covers where to delegate to agents and the operational pitfalls.