Tristen.

Solving the SPA Refresh Problem for React Apps Hosted on AWS S3 and CloudFront

May 21, 2024 (4mo ago)

Solving the SPA Refresh Problem for React Apps Hosted on AWS S3 and CloudFront

When building single-page applications (SPAs) with React and hosting them on AWS S3 and CloudFront, you might encounter a common issue: refreshing a page or directly accessing a route other than the home page results in an error. This happens because the server doesn’t know how to handle client-side routes. In this blog post, I’ll walk you through the steps to properly configure your AWS setup to ensure your SPA works seamlessly, even on refresh.

The Problem

If you’ve hosted a React SPA on AWS S3 and CloudFront, you might have seen this error:

<Error>
  <Code>AccessDenied</Code>
  <Message>Access Denied</Message>
  <RequestId>D1QH07X67AYNWZ54</RequestId>
  <HostId>azBROMZLMBqfRfRhTIUrIShpCJyFyhBR5FTnH+SBRhSe4xLMRIVjqUhcwMwLu/H1O6zTrOu3tU4=</HostId>
</Error>

This typically occurs when you refresh a page or try to directly navigate to a route other than /. The reason for this is that the server (S3 in this case) is trying to find a file corresponding to the route, but SPAs rely on client-side routing, which the server is unaware of.

The Solution

To solve this, we need to configure our S3 bucket and CloudFront distribution to always serve index.html for any route, allowing the client-side router to handle the navigation. Here’s how you can do it:

Step 1: Configure S3 Bucket

  1. Enable Static Website Hosting:

    • Go to your S3 bucket in the AWS Management Console.
    • Click on the Properties tab.
    • Enable Static website hosting.
    • Set both the Index document and Error document to index.html.
  2. Set Bucket Policy:

Ensure your bucket policy allows public read access.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}

Step 2: Configure CloudFront Distribution

  1. Set Default Root Object:

    • Go to your CloudFront distribution in the AWS Management Console.
    • Under General, set the Default Root Object to index.html.
  2. Edit Behavior Settings:

    • Navigate to the Behaviors tab.
    • Select the behavior for your default cache behavior (usually /).
    • Click Edit.
    • Ensure the following settings:
      • Viewer Protocol Policy: Redirect HTTP to HTTPS or HTTPS Only.
      • Allowed HTTP Methods: GET, HEAD, OPTIONS.
      • Cache Based on Selected Request Headers: None.
      • Object Caching: Use Origin Cache Headers.
      • Forward Cookies: None.
      • Query String Forwarding and Caching: None.
  3. Custom Error Responses:

    • Go to the Error Pages tab.
    • Create a custom error response:
      • HTTP Error Code: 404
      • TTL (seconds): 0
      • Custom Error Response: Yes
      • Response Page Path: /index.html
      • HTTP Response Code: 200
  4. Invalidate CloudFront Cache:

    • Go to the Invalidations tab.
    • Click Create Invalidation.
    • Enter /* to invalidate all cached files.
    • Click Invalidate.

Step 3: Clear Browser Cache

Your browser might cache the old version of the site. Clear the cache or use an incognito window to test the application.

Conclusion

By following these steps, you ensure that your React SPA can handle direct URL access and page refreshes correctly. This configuration tells AWS to always serve the index.html file for any route, allowing your client-side router to take over and display the correct view.

Deploying SPAs on AWS can be tricky, but with the right configuration, you can provide a seamless user experience. Happy coding!

Feel free to reach out if you have any questions or need further assistance.