Astro provides a built-in mechanism to define redirects in the redirects
property of the route
object.
export default defineConfig({
/* ... */
redirects: {
'/store': {
destination: 'https://store.example.com',
status: 302
},
'/old-slug': '/new-slug'
}
})
When the site is built these redirects turn into a index.html file at the specific path with a HTML meta redirect pointing to the new site. While this is a good solution for simple redirects, it has some drawbacks like the status code will not be returned by the original path, rather it will be 200 and the browser will flash the HTML of the redirect page briefly.
But Cloudflare Pages provides a better way to handle redirects with their _redirects
file. This file is a simple text file that lists the redirects in a specific format.
This file has a simple format: /from /to HTTP_STATUS_CODE
where the status code is either 301 or 302.
To recreate the same redirects in the _redirects
file, you can use the following:
/store https://store.example.com 302
/old-slug /new-slug 301
But wouldn’t it be nice to just maintain the Astro file and have it converted to the _redirects
file on build? This way it works on your local site and you don’t have to remember to convert it to the _redirects
file format.
To make this work you’ll need a custom integration which runs during the build process. You can use the following script to convert the Astro redirects to the _redirects
file format.
// astro-redirect-to-cloudflare-redirect.ts
import type { AstroIntegration } from 'astro'
import { writeFile } from 'fs/promises'
import { fileURLToPath } from 'node:url'
interface RedirectRule {
source: string
destination: string
status?: number
}
export default function redirectsToCloudflare(): AstroIntegration {
return {
name: 'astro-redirect-to-cloudflare-redirect',
hooks: {
'astro:build:done': async ({ dir, routes }) => {
console.log('\n🔄 Processing redirects for Cloudflare...')
// Extract redirects from routes
const redirects = routes
.filter((route) => route.type === 'redirect')
.map((route) => {
let destination = route.redirect
let status = 301
// Check if redirect is an object
if (typeof route.redirect === 'object' && route.redirect !== null) {
destination = route.redirect.destination
status = route.redirect.status || 301
}
const rule = {
source: route.component.toString(),
destination,
status
} as RedirectRule
console.log(` ↪️ ${rule.source} → ${rule.destination} (${rule.status})`)
return rule
})
console.log(`\n📊 Found ${redirects.length} redirects to process`)
// Format redirects for Cloudflare
const redirectContent = redirects
.map(({ source, destination, status }) => `${source} ${destination} ${status}`)
.join('\n')
// Write _redirects file
const outFile = fileURLToPath(new URL('./_redirects', dir))
await writeFile(outFile, redirectContent, 'utf-8')
console.log(`✅ Generated Cloudflare redirects file at: ${outFile}`)
}
}
}
}
Save this file to /utils/astro-redirect-to-cloudflare-redirect.ts
and then import it and add to the integrations
array in your astro.config.mjs
file.
import redirectsToCloudflare from './src/utils/astro-redirect-to-cloudflare-redirect'
// https://astro.build/config
export default defineConfig({
integrations: [
/* ... */
redirectsToCloudflare(),
],
redirects: {
/* ... */
}
})
From this point on you can maintain your redirects in the Astro config and they’ll be converted to the _redirects
file on build.