Simple SPA + API proxy setup with CloudFront

0

I have CloudFront distribution with 2 origins:

  1. S3 with my SPA build
  2. an API origin to an ALB. And any request with /api/ prefix goes to ALB. Everything else to S3.

I have 2 very simple requirements:

  1. If /api routes return some error (e.g., 404) , return it as-is.
  2. For all other routes, return 200 with index.html content in case of any error (e.g., 404 which can happen for every route since it's SPA),

This is such a simple requirement. But there is absolutely no way I found to implement this.

I tried everything:

  1. S3 allows setting an error document. But does not allow me to set a status code to be used in that case. So it always ends up returning 404.
  2. Error Response in CloudFront can do status code override too, but it overrides my API responses as well.
  3. I tried CloudFront functions to do this. But those don't run for Viewer Response if Origin returns 400 or higher status.
  4. Setting up a Lambda@Edge is really painful and seems like absolutely worst idea just to return one 200.

This article claims this exact model as the perfect architecture but it's like AWS team doesn't know it's not possible with their overcomplicated cloud: https://aws.amazon.com/solutions/guidance/improved-single-page-application-performance-using-amazon-cloudfront/

Please help! 🙏

2 Answers
0

You're right that this is a common setup for Single Page Applications (SPAs) with an API backend, and it can be frustrating to implement. However, there are ways to achieve your requirements using CloudFront and its associated services. Here's how you can address your two requirements:

  1. For API routes returning errors: You can use CloudFront's origin groups feature to set up separate error handling for your API origin. Create an origin group that includes your ALB origin, and configure it to pass through all status codes. This way, any errors from your API will be returned as-is.

  2. For SPA routes returning errors: To serve index.html with a 200 status code for all non-API routes that result in errors, you can use a combination of CloudFront's custom error responses and Lambda@Edge.

    First, set up a custom error response in CloudFront for your S3 origin. Configure it to respond with your index.html file for 4xx errors.

    Then, use a Lambda@Edge function associated with the Origin Response event. This function will run after S3 responds but before CloudFront sends the response to the viewer. In this function, you can check if the response is an error (status code >= 400) and if it's not an API route. If both conditions are true, modify the response to have a 200 status code.

Here's a basic outline of the Lambda@Edge function:

exports.handler = async (event) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    if (response.status >= 400 && !request.uri.startsWith('/api/')) {
        response.status = 200;
        response.statusDescription = 'OK';
    }

    return response;
};

While setting up Lambda@Edge might seem complex for a simple task, it provides the flexibility you need to handle this scenario correctly. It allows you to maintain the correct behavior for both your API and SPA routes.

Remember to configure your S3 bucket for website hosting and set the index document to index.html. This ensures that requests for directory paths (like /about/) will serve the index.html file.

By combining these approaches, you can achieve your requirements: API errors will be passed through as-is, while other routes will serve index.html with a 200 status code in case of errors, allowing your SPA to handle routing on the client side.
Sources
Deploy a React-based single-page application to Amazon S3 and CloudFront - AWS Prescriptive Guidance
Requests can't be served when sending requests to cloudfront alternate domain name | AWS re:Post
How to Solve: 403 Forbidden Error in Cloudfront (API Gateway)? | AWS re:Post

profile picture
answered 12 days ago
0

Hello ,

I understand your requirement but optimal solution is using Lambda@Edge Origin response function. This should be configured for your default behavior where the origin is S3, as this is the most effective way to achieve your desired architecture.

While alternatives like custom error pages and CloudFront functions exist, they won't provide the ability to handle redirections for a specific behavior. For instance, custom error pages would affect all behaviors globally rather than allowing specific handling for one behavior.

Your architecture with CloudFront will look like this :

Client - - - - - - > CloudFront - - - - > behaviour [1] Default - - - -> Origin S3, behaviour [2] API - - - - > Origin API

Configuration Overview:

  1. Default Behavior (S3 Origin):

    • Implement Lambda@Edge with Origin Response trigger
    • Handle response codes >= 400 with redirect to index.html
    • Return 200 OK status code
  2. API Behavior (/api/*):

    • Maintain original error handling
    • Error codes will be passed through to the client

Please refer the below documentation related to the redirection using status code you can use the snippets provided below to modify it according to your use case : [+] https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-update-error-status-examples

profile pictureAWS
answered 12 days ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions