Hosting a single page application or website on S3

on
Mar 09, 2022
in

In this blog post I will walk you through how to configure a single page application or website. In a previous blog Best practices for S3 web hosting and explaining why. Tibor Hercz explained the best practices for hosting a static website on S3.

As Tibor explained, there are some downsides on using a Origin Access Identity (OAI). But in some Single Page Applications (SPA) you will not experience this downside.

What is a single page application?

A single page application loads a single web document. Based upon your interaction with this page the content is updated.
The pages are rendered on your web browser. And more API calls are performed to fetch extra input. Due to this fact an SPA can be fast!

You can use the framework of your choice: angular, reactjs, vue, etc, etc.

Setting up the infrastructure

For simplicity, I am providing CloudFormation snippets.

First we will need an S3 bucket to host the application in. We need an OAI. And we need to grant it access yo the bucket:

  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private

  MyOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: Allows CloudFront to reach the bucket

  MyBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref MyBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action:
              - s3:GetObject*
              - s3:GetBucket*
              - s3:List*
            Effect: Allow
            Principal:
              CanonicalUser: !GetAtt MyOriginAccessIdentity.S3CanonicalUserId
            Resource:
              - !GetAtt MyBucket.Arn
              - !Sub ${MyBucket.Arn}/*

Next we will need the CloudFront distribution:

  MyDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        CustomErrorResponses:
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          Compress: true
          ForwardedValues:
            Cookies:
              Forward: none
            QueryString: false
          TargetOriginId: origin
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject: index.html
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: true
        Origins:
          - ConnectionAttempts: 3
            ConnectionTimeout: 10
            DomainName: !GetAtt MyBucket.RegionalDomainName
            Id: origin
            S3OriginConfig:
              OriginAccessIdentity:
                Fn::Join:
                  - ""
                  - - origin-access-identity/cloudfront/
                    - Ref: MyOriginAccessIdentity

Once you have set up the infrastructure. You need to build your SPA. And upload the build to the S3 bucket.
My assumption here is that you created an app with various routes/paths.

How does it work?

When you navigate to the hostname of the created CloudFront distribution. The distribution will fall back on the index.html file. This is because we have set the DefaultRootObject to index.html.
When you navigate in the application it looks like the browser is changing paths. But in fact it is only updating the address in the address bar.

Let say you created a /blog route. You navigate to this route. And you do a hard refresh! You expect to get a 404 Not Found error because there is no folder called blog.
But because we configured a CustomErrorResponses. That will redirect all 404 errors to the index.html file with a 200 response code.

This will make sure that the Single Page Application gets loaded again. It will see the location in the address bar and renders the correct content.

This does mean that you will need to handle unknown routes in your application.

Conclusion

When you want to host a Single Page Application you can use CloudFront with private buckets. You will get all the benefits of CloudFront while keeping the bucket private.

Joris has been working with the AWS cloud since 2009 and focussing on building event driven architectures. While working with the cloud from (almost) the start he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learn new things and experiment with them, because that is the way to learn and grow. In his spare time he enjoys running and runs a small micro brewery from his home.
Share this article: Tweet this post / Post on LinkedIn