Serve different origin as DNS CNAME value, depending on the IP Geolocation

321 Views Asked by At

I have Next.js Application that is deployed as Lambda Edge and Cloudfront that has S3 static files as origin. AWS Cloudfront URL is the value of example-domain.com CNAME.

Now I have a lot of changes to the Application and I want to deploy those changes. I don't want these changes to be override current deploy, instead I would like to create new environment and that environment should be available in few countries.

So in this case I would have 2 different Cloudfront URLS:

  • oldfeatures.cloudfront.net
  • newfeatures.cloudfront.net

Different origin url should be served depending on the geoloaction.

I am not sure that this is the right approach, but I am open for suggestions.

I am managing the domain settings in Cloudflare, all the rest is AWS environment.

How can I achieve this, without making changes to the Applications code.

1

There are 1 best solutions below

2
Ked Mardemootoo On

The easiest (and cost-effective) option would be to create a Cloudflare (CF) Worker and attach it to your subdomains. Probably better to attach it to both subdomains to ensure they can't access the newfeatures.cloudfront.net URL manually if they are 'not allowed' to. The second option would be to use CF Traffic Steering that comes with the CF Load Balancers feature. This option comes at a small cost and might not really fit your use case - it redirects based on regions rather than countries - I believe you'd need to fork more money and get an enterprise account for country based steering.

That said, the following option using a CF Worker JavaScript, will require you to have CF Proxy (the orange cloud) turned on.

Here are 3 options to satisfy your requirements - Recommending option 1 based on my understanding of your question.

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})


async function handleRequest(request) {
    return setRedirect(request) //Change to appropriate function 
}


// Option 1
// Return same newfeatures URL for specific countries based on a country list
// Useful if single URL applies for many countries - I guess that's your case
async function setRedirect(request) {
    const country = request.cf.country
    var cSet = new Set(["US", 
                        "ES", 
                        "FR", 
                        "CA", 
                        "CZ"]);

    if (cSet.has(country)) {  
        return Response.redirect('https://newfeatures.cloudfront.net', 302);
    }
    // Else return oldfeatures URL for all other countries
    else {
        return await fetch(request)
    }
}

// Option 2
const countryMap = {
    US: "https://newfeatures1.cloudfront.net", //URL1
    CA: "https://newfeatures2.cloudfront.net", //URL2
    FR: "https://newfeatures3.cloudfront.net"  //URL3
}

// Return different newfeatures URL for specific countries based on a map 
// Useful if more than two URLs
async function mapRedirect(request) {
    const country = request.cf.country
    
    if (country != null && country in countryMap) {
      const url = countryMap[country]
      return Response.redirect(url, 302)
    }
    // Else return old features for all other countries
    else {
      return await fetch(request)
    }
}

// Option 3
// Return single newfeatures URL for a single country.
// Much simpler one if you only have one specific country to redirect.
async function singleRedirect(request) { 
    const country = request.cf.country
    if (country == 'US'){
        return Response.redirect('https://newfeatures.cloudfront.net', 302)
    }
    // Else return oldfeatures URL for all other countries
    else {
        return await fetch(request)
    }
}