DEV Community

loading...
Cover image for How to handle a multilingual static site with Amazon CloudFront hosted on Amazon S3

How to handle a multilingual static site with Amazon CloudFront hosted on Amazon S3

Daniel Bayerlein
👨🏽‍💻 Web Dev & Consultant ❤️ JavaScript, React and GraphQL 😍 Full Stack Serverless 🏗 AWS Community Builder
・2 min read

The easiest way to host a static website on AWS is Amazon S3. In combination with Amazon CloudFront a really fast and cost-effective solution.

But if you run a multilingual static site, you run into the following problem: Amazon CloudFront allows you to specify a default root object, such as index.html, but this will not work in subdirectories.

Take a deeper look into the project structure of the different applications to make them easier to understand.

Single Page Application

my-s3-bucket
├── app.css
├── app.js
└── index.html
Enter fullscreen mode Exit fullscreen mode

Multi Page Application

my-s3-bucket
├── app.css
├── app.js
├── index.html
├── de
│   └── index.html
└── fr
    └── index.html
Enter fullscreen mode Exit fullscreen mode

When you access your CloudFront root URL / in the browser, the requested index.html page is delivered. Everything is fine. But if you call /de instead of /de/index.html in the browser, you will get an error page with the HTTP response status code 403.

A possible solution is to change your links and add /index.html to each link, but I would not recommend this. All right, but which solution is the better one? Use a Lambda function!

Use Lambda@Edge

Lambda@Edge is a feature of Amazon CloudFront that allows your code to run globally at AWS locations close to your users. 🚀

The Lambda function is very simple. Let's have a look at the code:

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request
  const uri = request.uri

  if (uri.endsWith('/')) {
    request.uri += 'index.html'
  } else if (!uri.includes('.')) {
    request.uri += '/index.html'
  }

  callback(null, request)
}
Enter fullscreen mode Exit fullscreen mode

This function inspects the incoming request from the client. Then rewrites the request so that CloudFront requests a default index object (index.html in this case) for each request URI that ends with / or does not contain a dot (.).

❗️ The AWS Lambda function must be deployed in the region us-east-1!

IAM

You also need to create an IAM role that can be assumed by the service principals lambda.amazonaws.com and edgelambda.amazonaws.com. Assign this role to your Lambda function.

{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "Service": [
               "lambda.amazonaws.com",
               "edgelambda.amazonaws.com"
            ]
         },
         "Action": "sts:AssumeRole"
      }
   ]
}
Enter fullscreen mode Exit fullscreen mode

Update CloudFront settings

Last but not least, it is necessary to set the default root object in the Amazon CloudFront settings to empty, as your Lambda function will handle it.

If you now call /de in the browser, you should see the correct page instead of the error page.


Note: Serverless Framework

As you probably noticed from my previous posts, I use the Serverless Framework in some projects.

If your application is not deployed in the AWS region us-east-1 you will have the following problem: The Serverless Framework does not allow you to deploy individual services in different regions. Therefore it is necessary to use two different serverless.yml manifests.


If you have any kind of feedback, suggestions or ideas - feel free to comment this post!

Discussion (0)