paint-brush
Implementing Geo-Location Redirects with AWS CloudFrontby@kvendingoldo

Implementing Geo-Location Redirects with AWS CloudFront

by Alexander SharovOctober 2nd, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Geolocation-based content is crucial for enhancing the user experience in modern web apps. Using AWS CloudFront and CloudFuntions, it is easy to implement and customize for your business requirements.
featured image - Implementing Geo-Location Redirects with AWS CloudFront
Alexander Sharov HackerNoon profile picture


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.

Understanding AWS CloudFront Geo-Location

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 ISO 3166-1 alpha-2.

  • 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 ISO 3166-2 code.

  • 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 ISO 3166-2 code.

  • 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 IANA time zone database format (for example, America/Los_Angeles).


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 official AWS documentation.

Setting Up Geo-Location Based Redirects

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.

Create a CloudFront Distribution

  1. Login to the AWS Management Console: Open the AWS Management Console and click to the CloudFront service.
  2. Create a New Distribution: Click "Create Distribution" and select the Web option. This allows you to set the distribution for both HTTP and HTTPS traffic.
  3. Configure Origin Settings: Set the origin parameters for your distribution. The origin could be an S3 bucket, an EC2 instance, or another source of content (in my case, an ECS Fargate instance with NLB).
  4. Set Up Default Cache Behavior: Configure the default cache behavior parameters, such as the viewer protocol policy and the allowed HTTP methods. Don’t forget to enable “All” Headers settings.
  5. Configure Distribution Settings: Provide a distribution name and configure other settings such as logging and pricing class.
  6. Create Distribution: Review your settings and click on “Create Distribution” to deploy it.

Create Geo-Location Based Redirects function

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:

  • Comprehensive Event Support (all four types of CloudFront events):
    • Viewer Request: Executed when CloudFront receives a request from the viewer.
    • Viewer Response: Triggered when CloudFront sends a response to the viewer.
    • Origin Request: Invoked when CloudFront forwards a request to the origin (e.g., S3, EC2, or a custom server).
    • Origin Response: Triggered when CloudFront receives a response from the origin.
    • This adaptability allows you to customize behavior across the request and response lifecycle, enabling advanced use cases like caching strategies, authentication, and content modification before it reaches the viewer.
  • Language Support: While CloudFront Functions only support JavaScript (ECMAScript 5.1), Lambda@Edge allows the use of multiple programming languages such as Node.js, Python, Go, and Ruby or your own docker images.
  • Greater Control of the Runtime Environment: Lambda@Edge provides a customizable runtime environment where you can define specific libraries, packages, or other dependencies. This enables advanced control over the execution environment, such as handling specific resource limits, setting timeouts, or even integrating with other AWS services like DynamoDB or S3.


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:

  • URL Rewrites or Redirects
  • Header Manipulation (e.g., adding security headers)
  • Simple Request Filtering (based on query parameters, cookies, or headers)


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:

  • Low latency: CloudFront Functions execute in less than 1ms, ensuring quick response times.
  • Cost efficiency: Functions are cheaper to run than Lambda@Edge for lightweight use cases.
  • Scalability: CloudFront Functions will scale automatically to handle millions of requests per second.


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.

Create a CloudFront redirect function

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:

  1. Open CloudFront Functions Console: Navigate to CloudFront Functions in the AWS Management Console.
  2. Create a New Function: Click Create Function and provide a name, such as GeoRedirectFunction.
  3. Write the Function Code: AWS CloudFront Functions use JavaScript (ECMAScript 5.1). Below is a sample code snippet that uses the CloudFront-Viewer-Country header to redirect users based on their country code:


// 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;

}


  1. Deploy the Function: After writing your function code, click Deploy to push the code to the edge locations.

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.

Best Practices for CloudFront Functions

  • Keep Code Lightweight: CloudFront Functions are designed for simple tasks, so ensure the logic is minimal, avoiding complex computations or heavy processing.
  • Test in All Regions: Make sure to test the function across multiple regions and countries to ensure redirects work as expected globally. Pay attention that not all CloudFront headers  might not be available for every IP address (e.g.: CloudFront-Viewer-City, CloudFront-Viewer-Metro-Code, CloudFront-Viewer-Postal-Code)
  • Security: Implement security headers like HSTS (HTTP Strict Transport Security) and ensure that your function is secure and handles headers correctly.
  • Fallback Redirects: Make sure to implement a fallback mechanism for users from countries not included in the redirect map (e.g., redirect them to a default site).
  • Optimize for Latency: Since CloudFront Functions execute at the edge, they are optimized for low-latency operations.
  • Geo-Location Accuracy: CloudFront’s geo-location detection is highly accurate, but it relies on IP-based location services. In rare cases, there may be inaccuracies due to proxy servers or VPNs.

Associate the CloudFront Function with the Distribution

  1. Select Your Distribution: Navigate to the CloudFront distribution you previously generated.

  2. Edit Cache Behavior: In the distribution settings, select the default cache behavior and click on "Edit".

  3. 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.

  4. Save Changes: Make the necessary changes and deploy the new cache behavior.



Testing and Monitoring

1. Test the Redirects

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.

2. Monitor the Distribution

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.


Conclusion

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.

References