Vincent CassiauBackend engineer

Astro and Cloudflare for a fast static site: ship only what the browser needs

Why I chose Astro SSG and Cloudflare for a bilingual content site, including the trade-offs of a static build and production measurements.

A mostly static content site is a strong fit for Astro and Cloudflare when pages can be built ahead of time, interactivity is limited to specific features, and no application server needs to assemble every response.

For my site, the requirements were narrow by design: publish in French and English, keep articles in Git, control metadata, and reuse presentation components. At launch, it needed no CMS, database, authentication, or request-time rendering.

I chose Astro to build the documents and Cloudflare Workers Static Assets to deliver them. Their scope matched the requirement. I assessed each layer by what it adds to the build, what it delivers to the browser, and what it requires in operations.

Define the output contract first

Framework selection should start with what a request is expected to produce.

Here, the browser needs a complete HTML document, one stylesheet, and the relevant assets. The content does not change according to the visitor’s identity or depend on data computed for each request. An editorial update can trigger a new build.

Static generation matches those constraints. Routes are known at build time, French and English variants are generated separately, content is validated before publication, and targeted interactivity can remain progressive.

The project uses Astro 7.1.3 with static output:

export default defineConfig({
  site: "https://vincentcassiau.com",
  output: "static",
  trailingSlash: "always",
  i18n: {
    defaultLocale: "en",
    locales: ["fr", "en"],
    routing: {
      prefixDefaultLocale: true,
      redirectToDefaultLocale: false
    }
  }
});

The /fr/ and /en/ URLs therefore represent separate documents. No server renders them again for each visit. This arrangement keeps canonical URLs, hreflang relationships, and internal links explicit. It also follows Google’s recommendation to use different URLs for each language version.

Astro adds structure at build time, not runtime

Handwritten HTML and CSS could produce an equally direct browser payload. For a few stable pages with no structured content or shared logic, that may remain the simplest option.

Astro gives me a framework for components, routing, and typed content while preserving that static output. This matches how I prefer to approach a content site: start with the document, then add JavaScript only where it provides a useful interaction.

By default, Astro renders its components to HTML without a client runtime. A framework component is hydrated in the browser only when a client:* directive requests it. Explicit client-side scripts can still be delivered without hydration. This distinction is central to Astro’s islands architecture.

These mechanisms become useful when several concerns must remain consistent: shared layouts, language variants, metadata, listing pages, RSS feeds, structured data, and publication rules.

Content Collections can describe metadata through a schema. On this site, every article must declare its locale, translation key, revision, domain, and publication status. The build rejects invalid entries, while TypeScript knows the resulting content shape.

The same guarantees could be assembled with custom scripts. Content loading and validation, routing, page generation, and types would then need to be maintained separately. Here, Astro brings those mechanisms together at build time without adding an application runtime.

React, Next.js, and Astro cover different scopes

React and Next.js operate at different levels. React provides a library for building interfaces, but does not define a rendering or deployment strategy on its own. The React documentation itself recommends starting a new application with a framework.

Next.js combines more capabilities: React applications, static rendering, server rendering, Server Components, application routes, and dynamic functions. They become useful for an interactive or personalized product.

Reducing Next.js to a fully hydrated single-page application would be inaccurate. With output: "export", Next.js generates an HTML file for each route. Compatible Server Components run during the build, and the resulting files can be served without a Node.js server.

Not every capability is supported by the static export. Request-dependent behavior and Server Actions require a server runtime. Headers and redirects can also be handled by the hosting infrastructure.

For my requirements, the main difference is scope. Astro renders components to HTML and requires an explicit decision for each interactive area. Next.js structures a React application that can grow towards more client-side or server-side behavior. Both can produce static output, but they organize a project around different needs.

For this site, content, bilingual routing, and presentation components were central. No feature required the broader application model provided by Next.js.

Cloudflare takes over at delivery time

Astro produces the artifacts. Cloudflare distributes them and can run narrowly scoped logic at the network edge. These layers solve different problems.

With Workers Static Assets, static files and Worker code are deployed as one unit. A URL matching an asset is handled asset-first by default. Worker code remains available for requests that do not match a file or for routes explicitly configured to run the Worker first.

The selected configuration gives that treatment to the root path only:

{
  "assets": {
    "directory": "./dist/",
    "binding": "ASSETS",
    "run_worker_first": ["/"],
    "not_found_handling": "404-page",
    "html_handling": "force-trailing-slash"
  }
}

The run_worker_first array can target the routes that execute Worker code before asset resolution. The neutral root reads the Accept-Language header and returns a temporary redirect to /fr/ or /en/. Its response includes Vary: Accept-Language and Cache-Control: private, no-store because the destination depends on the request.

Internal links point directly to a language-specific URL, and only those variants are intended for indexing. Localized pages matching the built files, the stylesheet, and other resources continue to use asset-first delivery. Security headers and cache policies are declared alongside the static files, without introducing an application server for every page.

Cloudflare can also configure caching, compression, the minimum TLS version, HTTPS redirection, and HSTS without changing Astro’s output. These concerns stay in the delivery layer rather than page code.

As of 25 July 2026, Cloudflare states that requests serving Static Assets are free and unlimited, with no additional charge for their storage. Worker invocations remain subject to the limits and pricing of the selected plan. This model suits a mostly static surface, provided the policy is checked again whenever costs are evaluated.

CyberVanguard uses the same separation between the Astro build and Cloudflare delivery for a distinct site.

Results from the live site

After publication, I measured the French and English home pages, notes indexes, and articles. Every page scored 100 for performance, accessibility, best practices, and SEO.

Surface Performance · Accessibility · Best practices · SEO Highest LCP Highest CLS Highest TBT Maximum transfer
FR/EN home pages 100 · 100 · 100 · 100 < 1.3 s 0 0 ms < 16 KiB
FR/EN notes 100 · 100 · 100 · 100 < 1.3 s 0 0 ms < 16 KiB
FR/EN articles 100 · 100 · 100 · 100 < 1.3 s 0 0 ms < 20 KiB

Astro builds complete HTML documents with a stylesheet, and Cloudflare delivers them without running an application server for every visit. The browser does not load a framework runtime.

The notes index adds only the JavaScript required for search, filtering, and sorting. That script is limited to the article listing. This interaction remains lightweight: the heaviest measured page stays below 20 KiB transferred, with a highest LCP below 1.3 seconds, no layout shift, and no recorded blocking time.

The costs of this architecture

Static generation moves work to build time. Every content change requires a new build and deployment.

That trade-off works when publishing is controlled and its frequency remains compatible with the release cycle. In this fully static configuration, data that must vary on every request would require an additional client-side or server-side layer.

Keeping content in Git provides a precise history and validation controlled by the project. It can become restrictive for an editorial team that needs previews, roles, scheduling, and a contribution interface. A CMS would then address a genuine requirement.

Bilingual publishing has a cost as well. Two well-structured URLs do not guarantee the quality of an adaptation or factual parity between both versions. The content schemas and project-specific build checks can detect a missing translation or a mismatched revision. Editorial review remains necessary.

Cloudflare is not necessary to serve a static directory. A host that can apply the expected headers and cache policy may be sufficient. Here, Cloudflare combines asset delivery with targeted handling of the neutral root.

Choose the smallest sufficient abstraction

In practice, I would look at four situations:

  • A few immutable pages with no structured content may remain direct HTML and CSS.
  • A static, multilingual content site with validated entries can use Astro as a proportionate framework.
  • A product organized around React, a team preference for that ecosystem, or a need for its application capabilities can make Next.js a coherent choice, including with a static export.
  • Personalized responses, authentication, or data computed on every request require a dynamic layer.

Cloudflare is a separate decision. It becomes useful when static artifacts need distributed delivery, cache policies, headers, and a small amount of targeted edge logic. Basic file hosting may not need it.

For my site, Astro is both a technical choice and a preference. I like its philosophy: start with HTML, add JavaScript only when it brings something useful, and keep a build I can inspect. Cloudflare complements that approach at delivery. It is not the only valid combination, but it is the one I want to maintain here.

Back to the notes