Using Astro server islands and Cloudflare geo headers for dynamic region-based content

A client needed to show different currencies and prices depending on the region they were visiting from. Here's how we used Cloudflare and Astro server islands to do that

webpage showing 'Cloudflare Geolocation Price: £29.99'

Create a new Astro project

npm create astro@latest astro-geo

For this simple case, set the Astro options as follows:

  • Empty project
  • No Typescript
  • Install dependencies
  • Initialize a new git repository

Add the Cloudflare adapter

npx astro add cloudflare

Enable server islands

This will be on by default in Astro 5.0, but for now add the following to astro.config.mjs

export default defineConfig({
  experimental: {
    serverIslands: true,
  },
});

Add a GeoLocation component

In the frontmatter, grab the Cloudflare cf-ipcountry header 1

---
const countryCode = Astro.request.headers.get("cf-ipcountry")
---

<h1>countryCode: {countryCode}</h1>

Add the component to index.astro as a server island

Just add the server:defer attribute: <GeoLocation server:defer />. Read more about Astro server islands

Create a new Cloudflare pages app

Publish your project to GitHub/Lab and create a Pages app from it via your Cloudflare dashboard

Check it’s working

Once built, when you visit the app in your browser you should see countryCode: GB or whatever your country is

Customise per region

Let’s show £ if you’re in the UK, if you’re in the EU and default to $ in the rest of the world

---
const countryCode = Astro.request.headers.get("cf-ipcountry")

const euCodes = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"];

// Default USD
let geolocatedSymbol = "$";
let geolocatedPrice = 19.99;

// If GB
if (countryCode === "GB") {
  geolocatedSymbol = "£";
  geolocatedPrice = 29.99;
}
// If in EU
else if (euCodes.includes(countryCode)) {
  geolocatedSymbol = "€";
  geolocatedPrice = 39.99;
}
---

<h2>Price: {geolocatedSymbol+geolocatedPrice}</h2>

Done!

Push your code up and check the build in your browser. If you have a vpn, try switching between US/UK/EU servers and reloading.

View live demo: astro-geo.pages.dev

If you need an Astro website, do get in touch!

webpage showing 'Cloudflare Geolocation Price: $29.99' webpage showing 'Cloudflare Geolocation Price: £19.99' webpage showing 'Cloudflare Geolocation Price: €39.99'

Footnotes

  1. It has been pointed out that we can just use Astro.request.headers.get("cf-ipcountry") rather the more cumbersome Object.fromEntries(Astro.request.headers)?.["cf-ipcountry"] we previously used, updated with thanks!