In the age of global digital services, geo-location based content is critical for improving user experience and engagement, especially if you implement any shops or subscription services that should be adopted to the local market.
AWS CloudFront is one of the most widely used content delivery network (CDN) systems. It includes certain necessary features to implement geo-location-based redirection out of the box, but not in a single click.
This article explores how to set up geo-location-based redirection with AWS CloudFront and provides a comprehensive guide to using it effectively.
AWS CloudFront is a CDN that distributes content across a global network of edge locations, reducing latency and improving load times for users. It caches content near to the user's location which improves performance. One of CloudFront's features is geo-location based content delivery, which allows you to modify content depending on the user's geographical location via a variety of approaches, including geo-location headers.
To determine user device type, IP address, or geographic location in an AWS-based project with CloudFront, no commercial API (such as Google API) is required. All of this can be configured on CloudFront using particular HTTP headers for requests received from viewers and forwarded to our origin, allowing us to obtain the necessary user information.
At the CloudFront level, the following headers can be set to determine the viewer's location. They are determined based on the viewer's IP address:
CloudFront-Viewer-Address
– Contains the IP address of the viewer and the source port of the request. For example, a header value of 198.51.100.10:46532 means the viewer's IP address is 198.51.100.10 and the request source port is 46532.
CloudFront-Viewer-ASN
– Contains the autonomous system number (ASN) of the viewer.
CloudFront-Viewer-Country
– Contains the two-letter country code for the viewer's country. For a list of country codes, see
CloudFront-Viewer-City
– Contains the name of the viewer's city.
CloudFront-Viewer-Country-Name
– Contains the name of the viewer's country.
CloudFront-Viewer-Country-Region
– Contains a code (up to three characters) that represent the viewer's region. The region is the first-level subdivision (the broadest or least specific) of the
CloudFront-Viewer-Country-Region-Name
– Contains the name of the viewer's region. The region is the first-level subdivision (the broadest or least specific) of the
CloudFront-Viewer-Latitude
– Contains the viewer's approximate latitude.
CloudFront-Viewer-Longitude
– Contains the viewer's approximate longitude.
CloudFront-Viewer-Metro-Code
– Contains the viewer's metro code. This is present only when the viewer is in the United States.
CloudFront-Viewer-Postal-Code
– Contains the viewer's postal code.
CloudFront-Viewer-Time-Zone
Contains the viewer's time zone, in
CloudFront is undoubtedly capable of providing many more informative headers that can be utilized to determine the user's device or other parameters. For a complete list of location headers, check the
First of all, we have to create an AWS infrastructure. Typically, users do it with any kind of automation tools like OpenTofu, Terraform, Pulumi or SDK, but in the scope of this article we’ll avoid it for simplicity.
AWS CloudFront provides two powerful options for running code at the edge: CloudFront Functions and Lambda@Edge. Each has its own strengths and is suited for different use cases.
Lambda@Edge offers extensive functionality, making it ideal for more complex edge processing needs. Key benefits:
CloudFront Functions are designed to handle lightweight tasks efficiently with near-instant execution (sub-millisecond). They are ideal for scenarios that require minimal processing, such as:
The key distinction is that CloudFront Functions can only be triggered on Viewer Request and Viewer Response events, making them a great choice for low-latency tasks that don't require origin interaction or deep processing logic.
Moreover, these functions are targeted for speed and cost, they scale easily to handle high-volume, real-time data. Additionally, they offer the following key benefits:
In summary, while Lambda@Edge offers more power and flexibility, it comes with slightly higher latency and cost compared to CloudFront Functions, which are designed for ultra-lightweight tasks like simple header modifications, redirects, or URL rewrites.
In this post, we use CloudFront function instead of Lambda@Edge's because it is much simpler in the case of redirects. To create it, do the following steps:
// code snippet 1.1
function handler(event) {
var request = event.request;
var headers = request.headers;
var uri = request.uri;
// Extract the 'CloudFront-Viewer-Country' header provided by CloudFront
var countryHeader = headers['cloudfront-viewer-country'] ? headers['cloudfront-viewer-country'].value : null;
// Check if the URI already starts with a two-character country code
function hasCountryCode(uri) {
// Split the URI into segments
var segments = uri.split('/');
// The first meaningful segment (after initial "/") should have two characters for a country code
if (segments.length > 1 && segments[1].length === 2) {
return true;
}
return false;
}
// If the URI already starts with a valid two-character country code, return the request unmodified
if (hasCountryCode(uri)) {
return request;
}
// If the CloudFront-Viewer-Country header is present
if (countryHeader) {
// Build the new URI by adding the country code as the first segment
var newUri = '/' + countryHeader + uri;
// Perform a 302 redirect to the new URI
return {
statusCode: 302,
statusDescription: 'Found',
headers: {
'location': { value: newUri }
}
};
}
// If no country code header is available, just forward the request unmodified
return request;
}
As demonstrated in code snippet 1.1, I used the simplest scenario for this post, the code does redirect from <url>/<path>
to <url>/<country_code>/<path>
. It is not a recommendation because the mechanism for redirecting or sending the country code is dependent on your own implementation. Changing code snippet 1.1 allows you to give <country_code>
via URI parameters like <url>/<path>?location=<country_code>
or do additional changes.
Another common use case is to return the CloudFront header in pure or modified format to the browser or client, rather as a pure redirect. It can be constructed using the "Viewer Response".
The basic function will look like this:
// code snippet 1.2
function handler(event) {
var request = event.request;
var response = event.response;
if (request.headers['cloudfront-viewer-country']) {
response.headers['x-country'] = request.headers['cloudfront-viewer-country'];
}
return response;
}
You may certainly change it, but in its most basic form, code snippet 1.2 simply returns the cloudfront-viewer-country
heading to the client as the x-country
header.
CloudFront-Viewer-City
, CloudFront-Viewer-Metro-Code
, CloudFront-Viewer-Postal-Code
)Select Your Distribution: Navigate to the CloudFront distribution you previously generated.
Edit Cache Behavior: In the distribution settings, select the default cache behavior and click on "Edit".
Add CloudFront Function to Viewer Request Event: Under the Function Associations section, add your newly created CloudFront function to the Viewer Request event. This ensures that the function is executed whenever a user requests content from CloudFront.
Save Changes: Make the necessary changes and deploy the new cache behavior.
To ensure that your geolocation-based redirects work properly, use a VPN or online resources to simulate queries from other regions. Users should be sent to the proper domain or page based on their current location.
Use AWS CloudFront's built-in monitoring tools to keep track of function performance, error rates, and request logs. You can also establish CloudWatch alarms to check for unexpected activity or high latencies.
Using CloudFront Functions for geo-location-based redirection or location determination is a fast, low-latency, and cost-effective way to deliver localized content. In a few steps, you can set up a CloudFront distribution, deploy a CloudFront function or Lambda@Edge, and begin routing users to region-specific content based on their location.
With these techniques, you can provide a more personalized and responsive digital experience to consumers around the world, which is critical for modern web apps.
Please let me know if you'd want to see the article with Lambda@Edge examples instead of CloudFront functions.