Why post bodies belong in object storage

Ghostlite splits every post in two. The metadata — title, slug, status, dates — lives in D1. The body lives in R2, as a single HTML file keyed by the post's ID.

At first glance this looks like extra work. Why not store the body in a TEXT column right next to everything else?

Databases are bad at big blobs

A post body can be tens of kilobytes of HTML. Put that in a row and every query that touches the table drags it along — even a query that only wanted the title. Object storage is built for exactly this shape of data: large, written rarely, read often.

  • List views stay fast because the rows stay small.

  • The body is fetched only on the post page, exactly when it is needed.

  • R2 reads are cheap and cache well at the edge.

The cache falls out for free

Because the body is addressed by a stable key, caching rendered HTML in KV becomes trivial. The cache key folds in the publish timestamp, so an edit busts the cache without a single line of explicit invalidation logic.

The best cache invalidation is the kind you never have to write.

Metadata in a database, content in a bucket — each tool doing the job it is genuinely good at.