Skip to content

CloudFront with Lambda@Edge returns 200 with empty body

1

Hi all,

I have set up a static website (Angular SPA) on S3 behind using CloudFront. To handle certain redirect I added a Lambda@Edge with a "Origin response" function association. In general this works well and behaves as expected. But for some reason, after some times, I receive a response with status 200 and an empty body from CloudFront. The X-Cache header of the response is set to "Hit from cloudfront" so it looks like the cache from CloudFront got invalid somehow, after I trigger a cache invalidation (/*) everything works fine for a few hours. I tested multiple cache behaviours but nothing seems to work in this case. As soon as I remove the Lambda@Edge the cache issue is gone.

Here you can see the code in my Lamda@Edge:

export const handler = async (event) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;
    
    let headers = response.headers;
    if (!headers) {
        headers = {};
    }
    headers['access-control-allow-origin'] = [{key: 'Access-Control-Allow-Origin', value: "*"}];
    headers['access-control-allow-methods'] = [{key: 'Access-Control-Allow-Methods', value: "GET, OPTIONS, HEAD"}];
    headers['access-control-allow-headers'] = [{key: 'Access-Control-Allow-Headers',value: "*"}];
    headers['access-control-max-age'] = [{key: 'Access-Control-Max-Age', value: "86400"}];
    response.headers = headers;
    
    if (request?.method === 'OPTIONS') {
        response.status = '204';
        response.statusDescription = 'No Content'
        return response;
    }
    
    if (request?.uri?.startsWith) {
        if (request.uri.startsWith('/404-not-found')) {
            response.status = '404';
            response.statusDescription = 'Not Found';
            return response;
        }
        
        const redirect = (path) => {
            const redirectResponse = {
                status: '301',
                statusDescription: 'Moved Permanently',
                headers: {
                  'location': [{
                    key: 'Location',
                    value: path,
                  }],
                  'cache-control': [{
                    key: 'Cache-Control',
                    value: "max-age=3600"
                  }],
                },
            };
            return  redirectResponse;
        }
        
        // categories
        if (request.uri.startsWith('/old')) {
            return redirect('/new'); 
        }
        // ... here are some additional urls

    }
    
    
    response.status = '200';
    response.statusDescription = 'OK';

    return response;
};

Has anyone experienced similar issues or has an idea how I might be able to solve this issue? I am testing this for the past few weeks and was not able to get around it for some reason.

Thanks a lot!

asked 2 years ago991 views

3 Answers
1

Your code shows that you're caching a 301 response for 1 hour (3600 seconds). A 301 is a permanent redirect. Browsers typically store this permanently, until the cache is cleared. I would try not setting a cache-control directive on that and see how it goes.

AWS
EXPERT

answered 2 years ago

EXPERT

reviewed 2 years ago

  • You are absolutely right, in case of a redirect this is far from ideal, i just copied the redirect part without really noticing that one. Unfortunately this has nothing to do (in my opinion) with the root problem, the redirected routes work perfectly even if the 200-empty-body response is sent back from CloudFront, it seems like the problem only occurs on the routes that are not redirected.

    I just tried to upload a version with the cache-control set on all requests, unfortunately it is really hard to test since the issue occurs random after some time. Some times it works for 4 days and on the other hand i stops working a few hours later...

  • The only other issue I can see is that you are explicitly returning 'No Content' if a 204 is detected. See if you're hitting that case by putting a random string in there in place of 'No Content'. It may be possible that an occasional request is sent using the OPTIONS method.

  • Also, can you share any of the Lambda@Edge function logs from when you've seen this occur? https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-logs.html

  • The lamba logs only includes the start and end report stuff. I am as well not receiving all logs, I get some logs if I invalidate the cache, but it seems CloudFront will not call the lambda again and somehow loses "the content" of the page itself in its own cache. I will remove the "No Content" stuff to test further, but the calls that are "failing" are clearly GET calls and not OPTIONS calls. The last logs are from Friday when I last invalidated the cache.

  • I just realised another weird behaviour. When this issue occurs, the favicon.ico that is returned suddenly has the index.html content.

1

As far as I can see, you're overwriting the response status code from the origin with a 200 OK status in all cases where a response isn't generated earlier in your function.

For example, if your origin returned a 403, 404, or 5xx error (such as a SlowDown response from S3) or a simple 301 redirect without content, your Lambda@Edge code would change it into a 200 OK, telling both the viewer and CloudFront that it's a valid, cacheable response:

...
    response.status = '200';
    response.statusDescription = 'OK';

Unless you want your site to change all responses into 200 OKs no matter what was requested or returned by the origin, you should either remove the status overwrite above, or apply a condition check for the cases where you want to apply it.

EXPERT

answered 2 years ago

EXPERT

reviewed 2 years ago

  • Unfortunately this is an intended behaviour at the moment. The website is an Angular SPA therefore the routing is done client side, therefore I have to return a 200 in any case (I know this is not ideal) to prevent 4xx issues if something like /path/to/this is initially called. The S3 is configured to always return index.html if "nothing" is found.

1

The issue you're experiencing with CloudFront returning a 200 status code and an empty body after some time, despite working correctly initially, could be related to how your Lambda@Edge function is interacting with CloudFront's caching behavior. Here are a few potential causes and solutions to consider:

  1. Cache Key: Ensure that your CloudFront distribution's cache behavior is configured to include all relevant headers in the cache key. If the cache key doesn't include headers that your Lambda function uses to make decisions, it could lead to inconsistent behavior.

  2. Origin Response Timing: The "Origin Response" trigger for Lambda@Edge runs after CloudFront receives a response from the origin. If your function is modifying the response in a way that affects caching, it could lead to unexpected results.

  3. Error Handling: Your Lambda function doesn't appear to have explicit error handling. Adding try-catch blocks and logging errors could help identify if there are any runtime issues causing the function to fail silently.

  4. Response Manipulation: Your function is always setting the status to 200 at the end, which might override the actual status from the origin. Consider removing this if it's not necessary for all responses.

  5. Cache-Control Headers: Ensure that appropriate Cache-Control headers are being set, either by your origin or by the Lambda function, to control how CloudFront caches the content.

  6. CloudFront Function: Consider using a CloudFront Function instead of Lambda@Edge for simple header manipulations and redirects, as it has less impact on caching behavior.

To troubleshoot:

  1. Add extensive logging in your Lambda function to track its execution path and any potential errors.
  2. Review CloudFront and Lambda@Edge logs in CloudWatch for any error messages or unexpected behavior.
  3. Verify that your cache policy and origin request policy are configured correctly for your use case.
  4. Test with a simplified version of your Lambda function to isolate the issue.

If the problem persists, you might need to reconsider your caching strategy or how you're implementing the redirects and header modifications.
Sources
Test and debug Lambda@Edge functions - Amazon CloudFront
Community | Enhancing security for Lambda function URLs
Protecting an AWS Lambda function URL with Amazon CloudFront and Lambda@Edge | AWS Compute Blog
CachePolicyHeadersConfig - Amazon CloudFront

answered 2 years ago

EXPERT

reviewed 2 years 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.