Content site architecture
Content site architecture: the decisions that matter
The architecture of a content site comes down to 4 choices: static/ISR rendering with on-demand revalidation behind the edge cache, teaser in cached HTML, gated body from an authed no-store endpoint, pre-generate image variants at upload; no per-request transforms and slugs are stable identifiers with automatic 301 redirects on change. Each is explained below with the alternatives that were rejected and what you accept by choosing it.
Last checked
Static/ISR rendering with on-demand revalidation behind the edge cache
Situation: A viral post can bring 100x normal traffic in minutes; rendering each request against Postgres is slow and expensive, yet editors expect changes to appear immediately.
Choice: Generate public pages statically, cache them at the edge with stale-while-revalidate, and revalidate by tag and path (and purge the edge) at publish, edit, unpublish and slug change.
- Rejected: Server-render every request: fresh but database load scales with readers.
- Rejected: Fully static export rebuilt on every change: fast reads but slow publish at thousands of pages.
- Rejected: Long TTLs without revalidation: cheap but stale corrections and unpublishes.
- Consequence: Readers rarely touch Postgres, so traffic spikes cost almost nothing.
- Consequence: Revalidation fan-out (home, tag, collection, feed, sitemap) must be complete and is tested.
- Consequence: Personalised UI is fetched client-side from private endpoints.
Teaser in cached HTML, gated body from an authed no-store endpoint
Situation: Static caching and paywalls conflict: if gated text is in cached HTML, one cache hit leaks paid content to everyone and crawlers.
Choice: Cached pages contain only the teaser and a placeholder; after canRead the client fetches the full body from a private, no-store endpoint; feeds, sitemaps, OG and search snippets use the excerpt only.
- Rejected: Hide gated text with CSS or client JS only: trivially bypassed with view-source.
- Rejected: Server-render gated pages per reader: correct but loses edge caching for the most valuable pages.
- Rejected: Cookie-vary caching: fragile and easy to mis-key.
- Consequence: Paid readers see a short loading step for the body.
- Consequence: Crawlers index the teaser unless Paywalled-content markup is configured.
- Consequence: Entitlement correctness depends on webhook-written memberships, tested end to end.
Pre-generate image variants at upload; no per-request transforms
Situation: Images are most of the bytes; resizing on demand is flexible but bills per transformation and adds latency and abuse risk.
Choice: A sandboxed batch job generates a fixed set of AVIF/WebP/JPEG widths under immutable keys once, served from the edge, with stored dimensions and blurhash.
- Rejected: On-demand resizing service: flexible but a per-use bill that traffic or attackers can inflate.
- Rejected: Serve originals: huge egress and failing Core Web Vitals.
- Rejected: Client-side resizing only: inconsistent quality and no bandwidth savings.
- Consequence: Storage grows by a bounded multiple per image.
- Consequence: New breakpoints require a backfill job under a processing budget.
- Consequence: Hot-linking cost is bounded by egress caps.
Slugs are stable identifiers with automatic 301 redirects on change
Situation: Renamed or moved articles break inbound links and lose search ranking if the old URL disappears.
Choice: Unique slugs per kind, a redirects table written in the same transaction as any slug or collection move, chain collapsing to one hop and a middleware lookup from a small cached map.
- Rejected: Immutable slugs: safe but forbids fixing typos and restructuring.
- Rejected: ID-only URLs: stable but poor for SEO and readability.
- Rejected: Manual redirects: forgotten in practice.
- Consequence: Moving a collection writes redirects for every published child (bounded and queued).
- Consequence: Redirect cache needs invalidation on change.
- Consequence: Slug history remains queryable for support.
How the data is indexed
Indexes are chosen from the questions the app asks most often. Each line below names a table and the question its main index answers.
- author_profiles: unique (slug) — author page URL.
- collections: unique (parent_id, slug) — sibling slugs are unique inside one parent.
- posts: unique (kind, slug) — one URL per slug and kind, lookup by slug on every page render.
- post_revisions: unique (post_id, rev) — revision numbers are unique, saves are idempotent.
- redirects: unique (from_path) — one redirect per old path, lookup on 404 and in middleware cache.
- tags: unique (slug) — tag page lookup.
- post_tags: (tag_id, post_id) — tag page: posts for a tag.
- media_assets: (owner_id, created_at DESC) — media library keyset page and storage quota.
What Forge64 provides underneath
Forge64 matches your description to one of 12 reference architectures, writes the plan and data model into the project, generates the app, and runs a quality gate over the code before you see it. Each app gets its own Postgres database and sign-in with no per-user fee. Hosting on Forge Cloud and Forge Payments are rolling out.
This is a reference design for the category, not a promise that every item is generated today. Built-ins such as email, background jobs, file uploads and search are coming soon, so anything here that needs them is something you add yourself for now.
FAQ
- What is the most important architecture decision for a content site?
- Static/ISR rendering with on-demand revalidation behind the edge cache. Generate public pages statically, cache them at the edge with stale-while-revalidate, and revalidate by tag and path (and purge the edge) at publish, edit, unpublish and slug change.
- Do I have to make these decisions myself?
- No. Forge AI plans them from your description and writes the decisions into the project as architecture records you can read and change.
- What do the alternatives cost me?
- For example, for "Teaser in cached HTML, gated body from an authed no-store endpoint": Hide gated text with CSS or client JS only: trivially bypassed with view-source. Server-render gated pages per reader: correct but loses edge caching for the most valuable pages.
Build your app with Forge64
One prompt to a running app with auth, database, hosting and payments. Start free, no credit card required.
Start building for free