Skip to main content
Back to blog
cloudflareperformanceastro

Edge-First Performance: Deploying Astro on Cloudflare Pages

A practical guide to shipping Astro static sites and server-rendered pages on Cloudflare's global edge network — with zero cold starts and sub-50ms TTFB worldwide.

By Sam Rivera
Edge-First Performance: Deploying Astro on Cloudflare Pages

What Makes Edge Deployment Different

Traditional hosting serves your site from a single origin — usually one data center region. A visitor in Tokyo hitting a server in Virginia adds 150–200 ms of round-trip latency before a single byte arrives. That latency compounds across every request, every redirect, and every API call.

Edge networks like Cloudflare Pages flip this model. Your site runs across 300+ data centers worldwide. The user in Tokyo is served from Osaka. The user in Paris is served from Frankfurt. TTFB (Time to First Byte) drops to single-digit milliseconds for static assets and under 50 ms for server-rendered responses.

Static vs. Server Output

Astro gives you a clean choice in astro.config.mjs:

export default defineConfig({
  output: 'static',     // or 'server' for SSR
  adapter: cloudflare({ imageService: 'compile' }),
});

Static output (output: 'static') generates pure HTML/CSS/JS at build time. Cloudflare Pages serves these files from its CDN. This is the fastest possible delivery — no server-side work happens at request time.

Server output (output: 'server') runs Astro inside a Cloudflare Worker for each request. This enables dynamic routes, authenticated content, and real-time data — all at edge latency.

For most landing pages and marketing sites, static output is the right choice. Add server output only when you need per-request personalization or API routes with secrets.

The imageService: "compile" Setting

This is the most common gotcha when moving to Cloudflare. Astro’s default image service uses Sharp, a native Node.js binding. Cloudflare Workers do not have Sharp available at runtime.

The fix is to process images at build time instead:

adapter: cloudflare({ imageService: 'compile' })

With imageService: 'compile', Astro’s <Image /> component transforms and optimizes your images during astro build. The output is plain <img> tags with pre-generated src values pointing to optimized static files. No Sharp at runtime, no Worker crashes.

Environment Detection

A common pattern for local development vs. production deployment:

// astro.config.mjs
const isProduction = process.env.NODE_ENV === 'production';

export default defineConfig({
  output: 'server',
  adapter: isProduction
    ? cloudflare({ imageService: 'compile' })
    : undefined, // Astro's built-in dev server handles SSR locally
});

Use process.env.NODE_ENV — not import.meta.env.DEV. The Vite runtime value is not available when astro.config.mjs is first evaluated by the Node.js process.

Wrangler for Local Preview

Once built, preview the Cloudflare environment locally before pushing:

# Build first
astro build

# Then preview with Wrangler (matches the actual Worker runtime)
wrangler pages dev ./dist --port 8788

This runs the exact same runtime as Cloudflare’s edge — V8 isolates, no Node.js globals, strict ESM. If something breaks in production, it will break here too. Running this before every deploy catches the majority of runtime issues.

Caching Strategy

Cloudflare caches static assets automatically. For dynamic routes, control caching with response headers:

// src/pages/api/data.ts
export const GET = () =>
  new Response(JSON.stringify({ ok: true }), {
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=60, stale-while-revalidate=300',
    },
  });

stale-while-revalidate lets Cloudflare serve a cached response immediately while fetching a fresh one in the background. This eliminates perceived latency for frequently requested data without sacrificing freshness.

Measuring the Gains

After deploying to Cloudflare Pages, measure with real-user geography:

The combination of Astro’s zero-JS-by-default architecture and Cloudflare’s edge network consistently produces perfect Lighthouse scores with no extra optimization effort.

All posts