Amazon Cloudfront Cache-Control: no-cache header has no effect after 24 hours

43,759

Solution 1

Verify that the CloudFront distribution's Minimum TTL is set to 0. If it's set to any other value, CloudFront won't respect the no-cache header and will still cache the file for the Minimum TTL. More details about the caching directives can be found here:

http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html

If this doesn't help, try to debug the actual HTTP request for index.html and post the response headers here so we can have a look at them.

Also, instead of using no-cache for the index.html file, you can try using

public, must-revalidate, proxy-revalidate, max-age=0

This will allow CloudFront to store the file on the edge location, but it will force it to revalidate it with the origin with each request. If the file hasn't changed, CloudFront will not need to transfer the file's entire content from the origin. This can speed up the response time, especially for larger files.

Solution 2

This is more of a comment, but a bit too long. Hopefully helps others that land here.

Cache busting via query parameter has drawbacks, although perhaps you can combat them all via Cloudfront behaviors. See https://stackoverflow.com/a/24166106/630614. Still, I would recommend unique filenames e.g. app.css?v=14113e2c764 becomes app.14113e2c764.css.

To respond to BradLaney's comment/issue: If you've updated cache-control headers and don't see the changes, it's because the origin item is already cached – invalidate it and you should see the new headers the next time you view the resource.

Regarding race condition when setting cache-control for S3 items, or just setting cache-control in general for an SPA, this is what's working well for my team:

# Sync all files with 1 week cache-control, excluding .html files.
aws s3 sync --cache-control 'max-age=604800' --exclude *.html dist/ s3://$AWS_BUCKET/
# Sync remaining .html files with no cache.
aws s3 sync --cache-control 'no-cache' dist/ s3://$AWS_BUCKET/
Share:
43,759
Adam
Author by

Adam

Updated on August 13, 2020

Comments

  • Adam
    Adam over 3 years

    I'm hosting a static website in S3 and using Cloudfront to cache files. I've essentially got 3 files with the following headers:

    • index.html (Cache-Control: no-cache)
    • app.js (Cache-Control: max-age=63072000, public)
    • style.css (Cache-Control: max-age=63072000, public)

    My html file uses query string parameters that get updated every time I update my css or js files. I've configured s3 to pass these parameters through and I've verified that it works to invalidate cached resources. My index.html file looks something like this:

    <html>
        <head>
            ...
            <link rel="stylesheet" href="app.css?v=14113e2c764">
        </head>
        <body>
            ...
            <script src="app.js?v=14113e2c764"></script>
        </body>
    </html>
    

    It seems to work great as I push updates all day, but when I come in the next morning and push my next change, The index.html file is out of date. Instead of having the correct ?v= parameter, it has the old one! The only way to fix it is to invalidate the html file manually. Then everything works for the rest of the day. The next day I have the same problem again.

    What's going on here?

  • Adam
    Adam over 10 years
    Looks like it was a race condition in the build script that uploaded to s3. The headers I was sending were inconsistent.
  • Nirav Gandhi
    Nirav Gandhi about 8 years
    Is it necessary to set minimum TTL to 0 after adding those metatags to index.html file? Can we have a min TTL > 0 for other files but not index.html?
  • dcro
    dcro about 8 years
    You can have different values for Minimum TTL if you define multiple Behaviors in CloudFront (e.g. one for index.html and another one for the rest of the files). Keep in mind though that the Minimum TTL configured in CloudFront is not usually the best solution to control how long a resource will be cached. You should usually specify the Cache-Control header on the responses produced by your origin server to control these and keep CloudFront's minimum TTL value to 0.
  • fullstacklife
    fullstacklife over 7 years
    24hr is the default behavior of Cloudfront, and cannot in any way be overridden. The good news is that if the object has not changed at the origin it will not refetch the object, it will use what it has in cache, and start the 24hr clock over again.
  • Jordan
    Jordan over 6 years
    Maximum TTL = 0 in the customize option does seem to be possible as well as changing the time from 24hr.
  • BradLaney
    BradLaney almost 6 years
    This doesn't work. I set the Cache-Control to that and amazon doesn't update anything. It keeps it cached from an in-determinant amount of time. I have my CloudFront settings cache settings set to "Use Origin Cache Headers"