Engineering

Reducing React app load times using Amazon CloudFront CDN

June 3, 2024
Viral Pasad
Sakshi Jain

Struggling with sluggish load times for your React app’s JavaScript chunk files? Are sporadic high load times causing frustration?

Load times can vary significantly depending on the user’s geographical location relative to your server, leading to inconsistent experiences. For instance, for a server located in North America, users in North America might experience quicker load times compared to those in Europe or Asia, causing frustration due to the unpredictable performance of your application.

Implementing effective caching strategies, such as using a Content Delivery Network (CDN) like Amazon CloudFront, can help ensure faster and more consistent load times for all users, regardless of their location. Additionally, optimizing client-side caching can further enhance the user experience by reducing unnecessary requests to the server/

Before diving into the setup, it’s essential to understand key services:

  1. Amazon CloudFront: Amazon CloudFront is a global content delivery network (CDN) service that speeds up the distribution of your React app’s static assets by caching them at edge locations worldwide.
  2. Amazon Route 53: Amazon Route 53 is a scalable and reliable DNS (Domain Name System) web service that translates human-readable domain names into IP addresses, ensuring users can access your React app by its domain name.
  3. Amazon Certificate Manager: Amazon Certificate Manager simplifies the process of provisioning and managing SSL/TLS certificates, ensuring secure communication between users and your React app by encrypting data transmitted over the internet.
  4. Nginx: Nginx is a high-performance web server and reverse proxy that efficiently serves static files and routes requests to your React app, enhancing its speed and reliability.

Let’s use the domain "https://app-dev.kapstan.io" as an example for this blog. Here’s a visual depiction illustrating the current architectural layout of the request flow for app-dev.kapstan.io. It encompasses the orchestration between Route 53, the load balancer (LB), an EC2 instance, and the Nginx server.


Let’s get started with the setup to add Amazon CloudFront to the above depicted architecture!

Create CloudFront distribution

  1. Access your AWS account and navigate to the “CloudFront” service page.
  2. Click on the “Create Distribution” button.
  3. In the “Origin” section, for the “Origin domain” field, specify the location where the static files are stored, which CloudFront will use to serve files to viewers.
    e.g., for app-dev.kapstan.io, Origin will be the Load Balancer which handles requests for the Nginx server.
  4. Refer to the attached screenshots for the necessary settings in the “Default cache behaviour” section.
  1. No changes are needed for the “Function associations” and “Web Application Firewall (WAF)” sections.
  2. In the “Settings” section,
    • In the “Alternate domain name (CNAME)” field, enter your website domain, e.g., app-dev.kapstan.io.
    • In the “Custom SSL certificate” field, select the certificate specifically issued for your exact subdomain,
      e.g., app-dev.kapstan.io and not *.kapstan.io.
      Note: For setting up CloudFront, AWS mandates that the certificate must be in the US East (N. Virginia) Region (us-east-1) in the same AWS account.
    • If desired, enable “Standard logging” to receive logs in an S3 bucket.
  3. Click on the “Create Distribution” button.
  4. The setup process may take a few minutes. The completion will be indicated when the “Last Modified” field changes from “Deploying” to a date and time stamp.
  5. Once completed, copy the value of the “Distribution domain name” field. This will be required in an upcoming section.

Handling of API requests

With all requests to app-dev.kapstan.io now routed through CloudFront, two significant issues arise:

  1. GET requests to /api/* would return cached responses, which is undesirable for dynamic API data.
  2. PUT, POST, DELETE etc. HTTP methods are not permitted as per the CloudFront configuration, resulting in HTTP 403 errors.

Immediate Solution: Adding a Behavior in CloudFront

To handle the API requests correctly, you can add a behavior in the CloudFront distribution to manage requests with the path pattern /api/*. Follow these steps:

  1. Locate and select the CloudFront distribution you’ve created.
  2. Go to the “Behaviors” tab and click on “Create Behavior” and set the settings as per this attached screenshots.
  1. Note: In the “Allowed HTTP methods” section, AWS mentions that ‘GET and HEAD methods are cached by default’. This is a bit confusing, but we have verified that CloudFront distribution does not return cached responses even for GET API requests for the behaviour created with Cache policy ‘CachingDisabled
  2. Save the behaviour

This immediate solution ensures that API requests are handled correctly by CloudFront. However, it may lead to unnecessary utilization of the allowed request count.

Long-Term Solution: Expose API server on a separate subdomain

To avoid the aforementioned issues and reduce the load on CloudFront, it’s recommended to have a separate endpoint for API requests. This way, API requests bypass AWS CloudFront and Nginx entirely.

  • Set up a separate subdomain specifically for API requests (e.g., api-dev.kapstan.io).
  • Configure your DNS settings to point the new API subdomain directly to your backend servers or load balancer.
  • Ensure your backend application is CORS (Cross-Origin Resource Sharing) enabled. This allows your frontend application to make requests to the separate API endpoint without facing cross-origin issues.

Testing CloudFront changes from local machine

To test CloudFront changes from your local machine, follow these steps:

  • For testing CloudFront changes from local machine, you need to make a new entry in the local “Hosts” file with the IP address of the CloudFront distribution domain and your website domain. (This is similar to adding a CNAME entry in the DNS service)
  • To get the IP address of the CloudFront distribution domain,
    • open Terminal and use the following command:
      ping <CloudFront distribution domain name>
    • Exit the ping command by pressing ‘control + C’
    • Copy the IP address as highlighted in the screenshot below
  • Next, edit the Hosts file on your local machine
  • Now, its time to verify if your website loads fine via CloudFront.
    • Open a new Chrome tab and open the browser developer tools (Right-click and select “Inspect” or press Command + Option + I).
    • Enter your website URL and press Enter.
    • In the Network tab of the developer tools, click on any HTML/JS request.
    • Look for the “Via” response header, which should indicate something like: Via: …9aa.cloudfront.net (CloudFront).
  • After confirming that the website is loading correctly via CloudFront, delete the entry you created in the Hosts file.
  • Proceed with updating the DNS settings in AWS Route53 or your DNS provider.

Link your website domain and the CloudFront distribution

Here, you need to create a CNAME entry in the DNS service. At Kapstan, we use AWS Route53 for this task. Follow these steps:

  1. Navigate to Route53 in your AWS account and open the hosted zone that handles kapstan.io
  2. In the “Records” tab, click on “Create Record”.
    • In the “Record name” field, enter the subdomain (e.g., app-dev).
    • In the “Record type” field, select CNAME.
    • In the “Value” field, enter the CloudFront distribution domain name
      (e.g., d1234567890abcdef.cloudfront.net).
  3. Click on “Create record”.
  4. The DNS propagation might take a few minutes.
  5. To verify if your website loads fine via CloudFront, follow the same steps as mentioned in the previous section. Basically, you have to look for the “Via” response header, which should indicate something like:
    Via: …9aa.cloudfront.net (CloudFront).

Setting Up Cache Invalidation for the CloudFront distribution

The final step in creating a CloudFront distribution is to set up cache invalidation, which should occur every time a new build is deployed. This step might differ based on your Continuous Deployment (CD) pipeline.

At Kapstan, we have configured Argo CD to run a post-deployment hook, which runs after the new pods come up and the old ones have been terminated. This post-deployment hook runs the cache invalidator which creates a new invalidation via the CloudFront API.

Bonus Section! Optimizing Client-Side Caching with Nginx

To ensure that your Nginx server sends the appropriate cache-control headers for optimal client-side caching, follow these guidelines. This ensures that static files are cached effectively by the browser and other mediaries, while dynamic content like your index.html file is always revalidated.

Step-by-Step Configuration

  1. Add the following configuration to your Nginx configuration file
    • For Non-HTML Files:
      • These files should be cached by the browser for a long duration to improve load times for repeat visits.
      • Add the following configuration to your Nginx configuration file (e.g., /etc/nginx/nginx.conf):

        # non-HTML fileslocation ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|otf)${root /usr/share/nginx/html;expires 1y;add_header Cache-Control "public, max-age=31536000, immutable";}
    • For HTML Files:
      • Your index.html file should not be cached to ensure that users always receive the latest version upon deployment of new builds.
      • Add the following configuration:

        # HTML files
        location /
        {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
        add_header Cache-Control "no-cache";
        }
  2. Add Meta Tags to Your index.html File
    • These meta tags ensure that the browser does not cache the index.html file, reinforcing the Nginx configuration.

      <head><!-- Other head elements --><meta http-equiv="cache-control" content="no-store" /><meta http-equiv="expires" content="0" /></head>

By following all these steps, you can significantly enhance the load times of your React application deployed on Nginx using Amazon CloudFront. Setting up a CDN helps in delivering your static assets swiftly across the globe, while handling API requests appropriately and optimizing client-side caching ensures that your users always get the latest content without unnecessary delays. With these optimizations in place, your application’s performance and user experience will see a marked improvement.

Viral Pasad
Senior Software Engineer @ Kapstan.

Simplify your DevEx with a single platform

Schedule a demo