10 min.

How to use CloudFront with static websites and SPAs

Learn how to utilize CloudFront with modern static websites and SPAs generated using Vue, React, Angular, or any other contemporary frontend framework

Deploying CloudFront with static websites or Single Page Applications (SPAs) developed with modern JavaScript frameworks like Vue, React, or Angular may require additional configuration to handle client-side routing.


Despite these considerations, CloudFront offers comprehensive benefits for efficient, secure, and customizable content delivery, making it an excellent choice for enhancing web performance and user experiences.



Why use CloudFront with static websites?


When you initially deploy your static website on Amazon S3, it is convenient to make the S3 bucket public, grant any user access permissions, enable static website hosting, and use the generated S3 URL as your website's entry point. However, this approach has several issues:



Custom URL


S3 URLs typically include Amazon's domain, which may not be suitable for professional applications. CloudFront can utilize custom domains.



Great global performance


S3's performance is satisfactory for users located near its Availability Zones, but it may lead to weak performance for a global audience. CloudFront leverages AWS Global Infrastructure's Edge Locations, providing unmatched performance.



Client-side routing control


S3 lacks client-side routing capabilities, which poses challenges in handling routing for modern frontend frameworks reliant on client-side routing. CloudFront can efficiently manage client-side routing through Lambda functions deployed directly from the CloudFront console.


These advantages make CloudFront a preferred choice when any of these issues are essential for your use case.



What Are the Options for Deploying a Static Website Using CloudFront?


There are two common options to deploy a static website into CloudFront, both utilizing S3 but differently:



S3 Website Endpoint


This option uses an S3 bucket as a web hosting which passes data to CloudFront but also makes CloudFront inherit some of the S3's built-in server-side routing. This is convenient because the static website will work straight away. However, there are two main issues:



Diluted SEO and broken pricing


Using S3 as web hosting means the bucket must be public, making your website accessible from two URLs: the S3 URL and the CloudFront distribution domain name URL. This duality can dilute SEO and lead to unpredictable costs due to different pricing for website traffic served from S3 and CloudFront.


Broken client-side routing


Another problem shared with the S3 web hosting solution, even if not used in conjunction with CloudFront, is that S3 web hosting adds a trailing slash to all URLs.


This occurs any time a file is not accessed directly by its path, which happens frequently because client-side routing in your frontend framework typically rewrites URLs to omit file extensions for cleaner-looking URLs and to remove the trailing slash. 


Unfortunately, these two mechanisms conflict with each other, resulting in a significant number of URLs that cannot be properly indexed by search engines.



Fix:  There is no native solution for the parallel URL access issue with S3 as web hosting. However, for broken client-side routing, you can use a URL rewriting function in CloudFront, which is easy to implement.



S3 bucket endpoint


This option uses S3 without the web hosting feature. S3 serves as a simple store, and CloudFront does not inherit any routing mechanism from S3. This approach can lead to broken URLs (e.g., refreshing some URLs might show an access denied error) even if permissions are set correctly.



Single URL


This solution provides a consistent URL since web hosting is disabled, avoiding confusion for search engines.



Better security


There's no need to make the bucket fully public. Instead, make your S3 bucket private and utilize Origin Access Control settings.



In the rest of the article, you will find two implementation solutions mentioned earlier, with details about why not to utilize the Error page as a redirection to 200 and a full implementation of the URL redirect Lambda function for easy copy and paste.



How to create S3 website endpoint for CloudFront implementation


Start by creating a new bucket, which for demonstration purposes we'll call blowstack-example-app


AWS S3 new bucket creation in N Virginia.


Upload your website files to this bucket and enable static website hosting in the Properties tab. This configuration requires specifying the 'index.html' and ‘404.html’ or other of your choice for the index and error documents, respectively.



AWS enables static website hosting.

It's common for static websites to be built into a folder named dist. Remember, if you upload this folder directly, S3's static website hosting won't work as expected because S3 expects the index and error documents to be in the root directory, not inside a nested folder. Thus, you should upload the contents of the dist folder to the root of the bucket.


Upon correctly setting up the bucket, you should see your static website URL in the Properties tab. 



AWS S3 hosting endpoint.



However, accessing this URL might initially result in an error due to the bucket's default private access settings.


An Error Occurred While Attempting to Retrieve a Custom Error Document



AWS S3 bucket access denied.


To make your website accessible, you'll need to adjust the bucket's permissions. First, in the S3 Permissions tab, remove the block on all public access. However, proceed with caution—making your bucket content publicly accessible can have security implications. 



AWS bucket all public access.


If you choose to make the bucket public, you must also edit the bucket policy to explicitly allow GetObject permission to everyone. This step enables public read access to your website files: 


	"Version": "2012-10-17",
	"Statement": [
	 		"Sid": "PublicReadGetObject",
	 		"Effect": "Allow",
	 		"Principal": "*",
	 		"Action": [
	 		 "Resource": [


Replace blowstack-example-app with your actual bucket name. After applying this policy, your website should be accessible via the provided URL.



How to create CloudFront distribution for S3 as a hosting endpoint


Create a new CloudFront distribution. For the origin, do not select items from the provided list. Instead, paste the URL from the S3 properties tab - specifically, the Bucket Website Endpoint.


Your website should be available shortly at the CloudFront Distribution domain name.


Please note that the trailing slash problem may exists because CloudFront inherits the routing mechanism from S3 hosting. Refer to the URL rewriting function for assistance, as described below.



How to impement CloudFront distribution with  S3 bucket endpoint


Ensure you have an S3 bucket with your static website uploaded. (For overlapping information, refer to the previous solution on using S3 as a web endpoint.)


Navigate to the CloudFront panel and initiate the creation of a new distribution. For the origin, select the S3 bucket containing your application files.


AWS CloudFront choosing S3 as origin.



I recommend selecting the "Origin Access Control (OAC)" option under bucket access. This feature ensures that content is served exclusively through CloudFront, not directly from S3. If you choose to block direct access on S3, ensure you enable the option to update the bucket policy accordingly.


You can generally leave the other settings as default and proceed to create the distribution.


After creation, you will be directed to the distribution's details on the General tab. Pay attention to the "Last Modified" field, which indicates when the distribution was last updated. Global updates to your distribution may take some time, so avoid immediate testing and refresh the distribution details to check for the latest changes


AWS CloudFront deploying.



Error page as a redirection to 200. Please don't do that!


Upon first accessing your distribution URL, you might encounter an "Access Denied" error due to CloudFront's specific routing behavior. This error indicates a 403 error code, which some might be tempted to rewrite to a 200 status code. However, this approach is not recommended as it can disrupt your application's functionality.



<Message>Access Denied</Message>



Instead of redirecting 403 to 200 errors, which only provides a partial solution and may interfere with your application's error handling, explore other routing and error handling strategies that are compatible with your application's architecture.


If you consider redirecting error responses, remember that doing so can cause issues, particularly with single-page applications (SPAs) that rely on client-side routing. Directly accessing nested URLs or refreshing the page might result in crashes if improper error handling or redirection is implemented.


Redirecting from a 403 to a 200 response by specifying a path (typically to /index.html) works only under certain conditions and may not be suitable for all applications, especially those using modern JavaScript frameworks that internally manage URL routing.



AWS CloudFront custom error page panel.



But why did such redirection work, even if only partially? The redirection works because a 403 error can trigger a redirection to the /index.html file, but this typically only functions correctly for the root URL. This behavior is well-described in AWS documentation on how the default root object works


As mentioned earlier, modern JavaScript frameworks often manipulate URLs internally to omit file extensions for a cleaner look. This is where the challenge lies: CloudFront, on its own, does not address the mismatch between the routing expectations of modern JavaScript frameworks and the way it handles error-based redirections



CloudFront url rewriting function


To ensure CloudFront properly handles direct visits to nested URLs, page refreshes or when S3 web hosting is disabled, you can implement a simple routing function. This function mimics the routing behavior of JavaScript frameworks. Follow these steps:


Navigate to the CloudFront console, and instead of selecting a specific distribution, look for the Functions option in the left-side hamburger menu.



AWS CloudFront main menu.



Create a new function that internally rewrites URLs to omit file extensions. The following code snippet is adapted from an AWS repository for a rewriting function:



function handler(event) {
    var request = event.request;
    var uri = request.uri;

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

    return request;



After creating the function, remember to publish it. Then, associate this function with the specific cache behavior in your distribution that relates to the app source.



AWS CloudFront function association modal.



After redeploying your distribution, your application should work seamlessly with direct visits to nested URLs and during page refreshes.


If issues persist (e.g., glitches when refreshing or entering the website), consider the following steps in S3:


Disable the static website hosting feature.

Perform a full invalidation of the CloudFront distribution to ensure all cached content is refreshed.

These measures help ensure that CloudFront correctly serves your application pages without encountering routing issues.



AWS CloudFront full distribution invalidation.