Troubleshooting

Our site serves different content to users based on their geolocation. What content is served depends on the “lang” query string in the URL.

For example:

  • users in the United States: no query string in URL
  • users outside of United States: ?lang=can query string in URL

Originally, we would grab the user’s IP address from the HTTP request to determine the user’s location. I’ve enabled the Cloudflare country code header in Network > IP Geolocation and I’m looking to use the country code from that header to add/remove the “lang” query param depending on the user’s location.

My worker (based off of developers.cloudflare.com/workers/examples/country-code-redirect):

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

async function handleRequest( req ) {
    if( 'GET' === req.method ) {
        return maybeRedirect( req )
    } else {
        return await fetch( req )
    }
}

async function maybeRedirect( req ) {
	const countryCode   = req.headers.get( 'cf-ipcountry' ),
		urlOrig 	    = req.url,
		urlObj 			= new URL( urlOrig )

	if( null !== countryCode ) {

		// only users outside of U.S. should have WPML param
		if( 'us' === countryCode.toLowerCase() ) {
			urlObj.searchParams.delete( 'lang' )
		} else {
            urlObj.searchParams.set( 'lang', 'can' )
		}		
	}

	let urlNew = urlObj.toString()	

	// only perform redirect if URL actually changed
	if( urlNew !== urlOrig ) {
		return Response.redirect( urlNew )
	} else {
		return await fetch( req )		
	}
}

In the worker preview tool, the redirects work when I manually add the cf-ipcountry header:

  • cf-ipcountry = “us” — sends me to non-query-string version of site
  • cf-ipcountry = “can” — sends me to ?lang=can version of site

I’ve added this worker using “.mydomain.com/” as the route; however, I’m not correctly being redirected when I access the live website:

  • I’m in the U.S. When I access to ?lang=can version of site, I’m not redirected to non-query-string
    version of site
  • when I use a VPN to mimic being in Canada and go to non-query-string version of site, I’m not redirected to ?lang=can version of site

Additional notes:

  • confirmed that the cf-ipcountry header is in the request when var_dumping $_SERVER
  • no Page Rules running for site
  • site plan is Pro

Any suggestions or ideas for what I could be doing wrong or what I could try out?

Thanks!

Hm that’s weird.

Have you already tried using req.cf.country instead to get the country code?

I’ve updated my script to check for req.cf.country first (and use that if available).

Right now, my worker:

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

async function handleRequest( req ) {
	if( 'GET' === req.method ) {
		return maybeRedirect( req )
	} else {
		return await fetch( req )
	}
}

async function maybeRedirect( req ) {
	const urlOrig 	= req.url,
		urlObj		= new URL( urlOrig )

	let countryCode = '',
		urlNew

	// check CF's native props in request
	if( 'object' === typeof req.cf && 'string' === typeof req.cf.country ) {
		countryCode = req.cf.country
	}

	// otherwise, check for country code in headers
	if( !countryCode.length ) {
		let headerCountryCode = req.headers.get( 'cf-ipcountry' )

		if( 'string' === typeof headerCountryCode ) {
			countryCode = headerCountryCode
		}
	}

	// only users outside of U.S. should have WPML param
	if( countryCode.length ) {
		if( 'us' === countryCode.trim().toLowerCase() ) {
			urlObj.searchParams.delete( 'lang' )
		} else {
			urlObj.searchParams.set( 'lang', 'can' )
		}
	}

	urlNew = urlObj.toString()

	// only perform redirect if URL actually changed
	if( urlNew !== urlOrig ) {
		return Response.redirect( urlNew, 301 )
	} else {
		return await fetch( req )
	}
}

Noting that I now have the following page rule:

mydomain.com/*
Cache Level: Standard, IP Geolocation Header: On

After testing some more, I realized I was able to get the query string to apply if I cleared Cloudflare’s cache and I visited a page that I hadn’t visited before. For example, when I visited mydomain.com using a VPN that had me in Canada, I was redirected to mydomain.com?lang=can.

However, if I disabled my VPN, visited mydomain.com (and was not redirected), then enabled my VPN and tried going to mydomain.com in an Incognito window, I was still not redirected (when I was previously redirected earlier).

So, based on the above, it seems like if pages are already cached by Cloudflare, then the worker doesn’t apply or take action.

I’d hate to set a Cache Level: bypass rule for all pages as we wouldn’t be able to use Cloudflare for redirection.

Any ideas on how to continue using this worker for redirection and still use Cloudflare’s caching?