GeoIP redirection worker

So, I’ve been tooling around with the worker playground and have come up with the following… Just sharing in case anyone wants to a) use this or b) pick holes in it :slight_smile:

//these countries go to .co.uk
var uk_countries = ['GB','JE','IM','GG'];
//these go to .ie
var ie_countries = ['IE'];
//these go to .eu.  Everyone else goes to .com
var eu_countries = ['AM','BH','CY','EG','IR','IQ','IL','IT','JO','KW','LB','OM','PS','YD','QA','SA','SY','TR','AE','YE'];
//these IP addresses don't get redirected
var bypass_ip = ['213.48.246.113'];
//user agents containing any of these strings don't get redirected
var bypass_ua = ['googlebot','bing'];


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

function inArray(needle, haystack) {
    var length = haystack.length;
    for(var i = 0; i < length; i++) {
        if(haystack[i] == needle) return true;
    }
    return false;
}

function byPass(request){
  if (inArray(request.headers.get('CF-Connecting-IP'),bypass_ip)){
    return true;
  }
  var length = bypass_ua.length;
  for(var i = 0; i < length; i++) {
        if(request.headers.get('user-agent').includes(bypass_ua[i]))
        return true;
    }
  return false;

}

function mapCountry(country_code){
  if (inArray(country_code,uk_countries)){
    return 'www.website.co.uk';
  }
  if (inArray(country_code,ie_countries)){
    return 'www.website.ie';
  }
  if (inArray(country_code,eu_countries)){
    return 'www.website.eu';
  }
  //everyone else gets US
  return 'www.website.com';
}


/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function Redirect(request) {
  var url = new URL(request.url);

  correctHost = mapCountry(request.headers.get('CF-IPCountry'));
  if (byPass(request)){
    correctHost = url.hostname;
  }
  if (correctHost !== url.hostname){
    url.hostname = correctHost;
    console.log('redirecting to '+url.href);
    return new Response('', {
        status: 301,
        headers: {
          'Location': url.href
        }
      })
  }

  console.log('no redirect');
  const response = await fetch(request)
  return response
}
6 Likes

Very nice! I’ve got a similar problem to solve and this helps a lot.

1 Like

Thank you so much for your code! I tried to modify the above code to fit my site needs (actually a simpler implementation), but got a “worker re-directed too many times” error when I tried to preview it. If anyone has a suggestion as to how to resolve this it would be greatly appreciated; below is the code I tried (websiteurl used for site url below, I have a 2 link limit that prevents me from using actual url).

Thanks!

//these countries go to us.websiteurl
var us_countries = [‘US’,‘GU’,‘PR’,‘UM’,‘VI’];
//these IP addresses don’t get redirected
var bypass_ip = [‘1.1.1.1’];
//user agents containing any of these strings don’t get redirected
var bypass_ua = [‘googlebot’,‘bing’];

addEventListener(‘fetch’, event => {
event.respondWith(Redirect(event.request))
})

function inArray(needle, haystack) {
var length = haystack.length;
for(var i = 0; i < length; i++) {
if(haystack[i] == needle) return true;
}
return false;
}

function byPass(request){
if (inArray(request.headers.get(‘CF-Connecting-IP’),bypass_ip)){
return true;
}
var length = bypass_ua.length;
for(var i = 0; i < length; i++) {
if(request.headers.get(‘user-agent’).includes(bypass_ua[i]))
return true;
}
return false;

}

function mapCountry(country_code){
if (inArray(country_code,us_countries)){
return ‘us.websiteurl’;
}
//everyone else gets vantage.network
return ‘websiteurl’;
}

/**

  • Fetch and log a given request object
  • @param {Request} request
    */
    async function Redirect(request) {
    var url = new URL(request.url);

correctHost = mapCountry(request.headers.get(‘CF-IPCountry’));
if (byPass(request)){
correctHost = url.hostname;
}
if (correctHost !== url.hostname){
url.hostname = correctHost;
console.log(‘redirecting to ‘+url.href);
return new Response(’’, {
status: 301,
headers: {
‘Location’: url.href
}
})
}

console.log(‘no redirect’);
const response = await fetch(request)
return response
}

If I change this URL to www.website.com/eu I get

Uncaught (in promise) Error: internal error
at Redirect (VM8 worker.js:65)
at VM5 worker.js:13

Looks like it’s because of the “/”. Any idea how to fix this?

@foreverunique I also have the same issue listed by @it1109 . If anyone could advise, I would be super grateful. Cheers

You need consider google bot as a real visitor…

This solution from https://community.cloudflare.com/u/sandro
seems to work perfectly.

Untested (and a bit rough), but have you tried this?

Also, make sure it is mapped only to / without any wildcards, otherwise you enter a loop.

async function handleRequest(request)
{
	return request.cf.country == 'IT' ? Response.redirect(request.url + '/it') : fetch(request);
}

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

This solution from https://community.cloudflare.com/u/sandro
seems to work perfectly.

Untested (and a bit rough), but have you tried this?

Also, make sure it is mapped only to / without any wildcards, otherwise you enter a loop.

async function handleRequest(request)
{
	return request.cf.country == 'IT' ? Response.redirect(request.url + '/it') : fetch(request);
}

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

Hi!

sorry, I can’t redirect properly.

my site is www. (companyname) .com / (country-code) / (pages)
but I can only redirect to a different domain, not to the right page. What am I doing wrong?

Thanks

Post your exact code. There could so many issues there is no better way…

sure!

for now I used same datas in example, only changing the iso country code and redirect destination:

function mapCountry(country_code){
  if (inArray(country_code,uk_countries)){
    return 'xxxxx.com/country-blocked/';
  }
  if (inArray(country_code,ie_countries)){
    return 'xxxxx.com';
  }
  if (inArray(country_code,eu_countries)){
    return 'xxxxx.com';
  }
  //everyone else gets US
  return 'xxxxx.com';
}

Grazie

There isn’t much to go about, especially since the code posted before was not really well formatted.

Aren’t you trying to modify the path? Those seem to return the hostname.

This is the full code:

//these countries go to REDIRECT
var uk_countries = ['IT'];
//these go to .ie
var ie_countries = ['IE'];
//these go to .eu.  Everyone else goes to .com
var eu_countries = ['AM','BH','CY','EG','IR','IQ','IL','JO','KW','LB','OM','PS','YD','QA','SA','SY','TR','AE','YE'];
//these IP addresses don't get redirected
var bypass_ip = ['213.48.246.113'];
//user agents containing any of these strings don't get redirected
var bypass_ua = ['googlebot','bing'];


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

function inArray(needle, haystack) {
    var length = haystack.length;
    for(var i = 0; i < length; i++) {
        if(haystack[i] == needle) return true;
    }
    return false;
}

function byPass(request){
  if (inArray(request.headers.get('CF-Connecting-IP'),bypass_ip)){
    return true;
  }
  var length = bypass_ua.length;
  for(var i = 0; i < length; i++) {
        if(request.headers.get('user-agent').includes(bypass_ua[i]))
        return true;
    }
  return false;

}

function mapCountry(country_code){
  if (inArray(country_code,uk_countries)){
    return 'xxxx.com/country-blocked/';
  }
  if (inArray(country_code,ie_countries)){
    return 'xxxx.com';
  }
  if (inArray(country_code,eu_countries)){
    return 'xxxx.com';
  }
  //everyone else gets US
  return 'xxxx.com';
}


/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function Redirect(request) {
  var url = new URL(request.url);

  correctHost = mapCountry(request.headers.get('CF-IPCountry'));
  if (byPass(request)){
    correctHost = url.hostname;
  }
  if (correctHost !== url.hostname){
    url.hostname = correctHost;
    console.log('redirecting to '+url.href);
    return new Response('', {
        status: 301,
        headers: {
          'Location': url.href
        }
      })
  }

  console.log('no redirect');
  const response = await fetch(request)
  return response
}

Yeah, it seems you aren’t actually redirecting what you want. You need to make the redirect for the path…

Sorry, is not clear for me… why my redirect is not correct?

Because the code you are using redirect via hostname. You need to modify it so it redirects only the path.

Hello, sorry, I forget to respond… Now all works! thanks

1 Like

Hi, can you please share your fixed code? Thanks!

sure:

//these countries go to REDIRECT
var it_country = ['IT','KR','DE','NL','CN'];
//these IP addresses don't get redirected
var bypass_ip = ['x.x.x.x'];
//user agents containing any of these strings don't get redirected
var bypass_ua = ['googlebot','bing'];


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

function inArray(needle, haystack) {
    var length = haystack.length;
    for(var i = 0; i < length; i++) {
        if(haystack[i] == needle) return true;
    }
    return false;
}

function byPass(request){
  if (inArray(request.headers.get('CF-Connecting-IP'),bypass_ip)){
    return true;
  }
  var length = bypass_ua.length;
  for(var i = 0; i < length; i++) {
        if(request.headers.get('user-agent').includes(bypass_ua[i]))
        return true;
    }
  return false;

}

function mapCountry(country_code){
  if (inArray(country_code,it_country)){
    return 'xxx.xxx.com';
  }
  //everyone else gets US
  return 'xxx.com';
}


/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function Redirect(request) {
  var url = new URL(request.url);

  correctHost = mapCountry(request.headers.get('CF-IPCountry'));
  if (byPass(request)){
    correctHost = url.hostname;
  }
  if (correctHost !== url.hostname){
    url.hostname = correctHost;
    console.log('redirecting to '+url.href);
    return new Response('', {
        status: 301,
        headers: {
          'Location': url.href
        }
      })
  }

  console.log('no redirect');
  const response = await fetch(request)
  return response
}
3 Likes

Where to add this code? @MSA2018