Locale Switching for Region Specific Content in Apache

Published

We launched a website a few months ago that featured region specific content for users in North America, Europe, India, and China.

Our strategy was to redirect users to the appropriate site given their location (which we referred to this as the user’s “default locale”).

Locale Switcher deployed as part of the CasellaSolutions.com website

Locale switcher created for the Casella Solutions site

Once viewing the site, the use could then choose a “preferred locale” using a locale switcher (screencap shown above).

Below was the resulting order of precedence when determining which localized content to serve to the user:

  1. User’s Preferred Locale – detected by the presence of a “preferred locale” cookie on requests
  2. User’s Location – determined by the examining request headers from the users’s browser or intermediary CDN
  3. Default Site – served if no locale cookie or location information was present

While this was the best option given our situation, other alternatives for Content Negotiation exist, with some detailed below.

Determining a User’s Location

1. The browser’s “Accept-Language” header

The “Accept-Language” header included on requests specifies which languages are preferred by the user’s browser (presumably derived from the operating system’s locale settings). This header can be examined by Apache rewrite rules or other dynamic scripts to assess which content to display to the user.

Screen grab of the Chrome Developer Tools with a request selected and the "accept-language" header highlighted.

2. Location Headers provided by CDNs

Many of the major CDNs can attach headers to inbound HTTP requests that specify the country of origin for the request (including Amazon CloudFront1 and Cloudflare2). These headers can be read by Apache or other parts of the infrastructure when determining which version of content to serve to the user.

Below is an example of Apache rewrite rules that redirect users based on the “CLOUDFRONT-VIEWER-COUNTRY” header. The UK site is the default locale, with US and CN specific redirects employed for those locations.

# Default - redirect to UK if location is not US or CN
RewriteCond %{REQUEST_URI} ^(/)?$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} !^$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} ^!US$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} ^!CN$
RewriteRule ^.*$ /uk/en.html [R=302,L]

# redirect to United States site if browsing from US
RewriteCond %{REQUEST_URI} ^(/)?$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} !^$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} ^US$
RewriteRule ^.*$ /us/en.html [R=302,L]

# redirect to Chinese site if browsing from CN
RewriteCond %{REQUEST_URI} ^(/)?$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} !^$
RewriteCond %{HTTP:CLOUDFRONT-VIEWER-COUNTRY} ^CN$
RewriteRule ^.*$ /cn/en.html [R=302,L]

Notice that temporary 302 redirects are used here; while preferred for SEO purposes, 301 redirects are cached by browsers and will prevent users from being redirected correctly if their location or preferences change (e.g. cookie value changes when selecting a different preferred locale).

3. IP Address Geo-location Services

Efforts can be taken to map a user’s IP address to their geographical location, revealing which country they are browsing from. The Maxmind Geo IP database is the defacto open source tool for accomplishing these lookups. It has its own Apache module available, along with open source libraries to facilitate it’s use in several back-end languages such as PHP or NodeJS.

Other services exist, with some examples listed in this thread.

Enforcing a User’s Preferred Locale using cookies

One approach for implementing “sticky” Locale Switching is to set a cookie in the user’s browser that contains the user’s “preferred locale”. This cookie will then be read by the webserver (Apache in our case), and redirects to the correct locale can be served as needed.

For example, if the user selects China as their preferred locale, but then makes a request for the US homepage, the web server will see the cookie specifying CN as the preferred locale, and the user will be redirected to the Chinese homepage.

Here is an example JavaScript snippet that can be used to set the user’s preferred locale into a cookie:

// determine expiration date
var date = new Date()
date.setTime(date.getTime() + (180*24*60*60*1000)) // expires in 180 days

// set cookie containing preferred locale
document.cookie = "preferred_locale_code="+ preferredLocaleString + "; expires=" + date.toUTCString() + "; path=/"

Once the cookie is set, it will be passed and read by the web server. Here is a set of Apache rules to perform redirection based on a “preferred_locale_code” cookie:

# redirect to UK based off set cookie

  # request made to the domain root
  RewriteCond %{REQUEST_URI} ^/$
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^UK$
  RewriteRule (.*) /uk/en.html [R=302,L]

  # with path
  RewriteCond %{REQUEST_URI} ^/(?!uk).*/en.*
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^UK$
  RewriteRule ^(.*)/en(.*) /uk/en$2 [R=302,L]

# redirect to US based off set cookie

  # request made to the domain root
  RewriteCond %{REQUEST_URI} ^/$
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^US$
  RewriteRule (.*) /us/en.html [R=302,L]

  # with path
  RewriteCond %{REQUEST_URI} ^/(?!us).*/en.*
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^US$
  RewriteRule ^(.*)/en(.*) /us/en$2 [R=302,L]

# redirect to CN based off set cookie

  # request made to the domain root
  RewriteCond %{REQUEST_URI} ^/$
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^CN$
  RewriteRule (.*) /cn/en.html [R=302,L]

  # with path
  RewriteCond %{REQUEST_URI} ^/(?!cn).*/en.*
  RewriteCond %{HTTP_COOKIE} preferred_locale_code=([A-Z][A-Z])
  RewriteCond %1 ^CN$
  RewriteRule ^(.*)/en(.*) /cn/en$2 [R=302,L]

If using a CDN or reverse-proxy, make sure it is configured to pass you cookie (“preferred_local_code” in this case) on through to the web server, otherwise the redirection logic will not work!

Wrapping Up

This solution worked well for us because our locale-specific sites all had roughly the same content. I could see these rules being problematic for sites where localized content is drastically different, as Apache would try to redirect to non-existent pages when locale switching.

References

  1. Deliver Custom Content With CloudFront
  2. What does Cloudflare IP Geolcation do?

Comments

Loading comments..

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *