Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defeating the browser cache #346

Closed
jtlapp opened this issue Dec 16, 2018 · 14 comments
Closed

Defeating the browser cache #346

jtlapp opened this issue Dec 16, 2018 · 14 comments

Comments

@jtlapp
Copy link

jtlapp commented Dec 16, 2018

I rolled my own little site generator and discovered a not-so-little problem. When I roll out a new css or js file, clients have to wait for the local cached copy to expire before they get the new version. Even while developing, I find that forcing a reload of the page in Chrome doesn't always reload the files.

It seems that the standard solution is to tack some version indicator to the query portion of the URL. E.g. /site.js?2 or site.js?v=2.

I have also discovered that Twitter caches Twitter card images forever, even if I force a re-validation of the page URL. The recommended solution is also to tack a version to the image URL. E.g. /images/pic.jpg?2.

The simplest solution is to bump the version number of every resource file with every build. But that's crazy. The ideal solution is to only bump the versions of the files that changed. Complicating matters, I construct some of my links programmatically from data files, so the generator wouldn't even know whether a link could be pointing to a changed file until after the link is generated and the linked-to file compared to a prior version. I started imagining a two-phase solution: track links while generating the static files, and then modify already-generated links for files that changed. (And there's also the business of determining whether a file changed relative to the prior upload.)

I'm ready to graduate to a more thought-through site generator and landed on Eleventy as a possibility. This is only one of the big-boy features I'm looking for, but it seems to be the hardest one to find.

How does Eleventy force browsers to reload and ignore cache?

P.S. I do find it necessary to force reload rather than wait for cache expiration. Should HTML, CSS, or JS ever get out of sync, the results can be disastrous.

(Oh, I should mention that it was easy to deal with bumping the image version. I just keep data on my images and append the version to each image URL. Other resources are more problematic because I absolutely don't want to forget to keep them in sync. It's not as crucial that images be kept in sync.)

@jtlapp
Copy link
Author

jtlapp commented Dec 16, 2018

Come to think of it, a build number would work fine, even it makes for strange version numbers in the URLs. But that requires maintaining state between builds. If nonsensical numbers are okay, then the query suffix could be the Unix time or even a random number. Maybe this is simple after all?

@jtlapp
Copy link
Author

jtlapp commented Dec 16, 2018

It looks like I found a solution that doesn't require site generator support. If you do have a standard way to do this in Eleventy, please let me know. Otherwise I'm happy to close this.

@pamtbaau
Copy link

It looks like I found a solution that doesn't require site generator support.

Would you mind sharing your solution?

@zachleat
Copy link
Member

On my site I use a versioned directory that has far future expires headers set on everything in there. Then when my JS/CSS/etc changes, I bump the version and deploy to the new folder. This allows both the old and new versions to keep working side by side.

e.g.: https://www.zachleat.com/web/dist/1.0.10/defer.min.js

It’s an old trick but it works well https://developer.yahoo.com/performance/rules.html#expires

@zachleat
Copy link
Member

Note that Eleventy does not handle this transparently yet, but would support something like this when we deliver #272.

@zachleat zachleat added the waiting-to-close Issue that is probably resolved, waiting on OP confirmation. label Dec 17, 2018
@pamtbaau
Copy link

Was just looking at the 'plugin' section from the docs and saw this link: eleventy-plugin-cache-buster

Haven't looked at its code yet, but the description seems to address the 'cache busting' issue.

@pamtbaau
Copy link

pamtbaau commented Dec 17, 2018

@zachleat Just curious...

On my site I use a versioned directory that has far future expires headers set on everything in there.

Have you added a .htaccess to the root folder for setting the cache-control on file types?

Then when my JS/CSS/etc changes, I bump the version and deploy to the new folder.

Have you added some package to your css/js workflow to achieve this?

@zachleat
Copy link
Member

I think I stole a few apache rules from HTML5 boilerplate to do this yeah:

My website code is up on github if you don’t mind digging through it but I wouldn’t 😅 https://github.com/zachleat/zachleat.com/blob/master/.htaccess#L73

Basically, I use those rules and then create new files under a new versioned url rather than using a cache busting ref. I use the package.json version of my site to decide what the folder is named. So when I bump versions, it bumps the delivered assets for the site. Note that eleventy makes this moderately easier with the pkg global variable: https://www.11ty.io/docs/data/#eleventy-provided-data-variables

@zachleat
Copy link
Member

@pamtbaau If you’d like to file a new issue requesting better documentation for this—please do!

@zachleat
Copy link
Member

This is an automated message to let you know that a helpful response was posted to your issue and for the health of the repository issue tracker the issue will be closed. This is to help alleviate issues hanging open waiting for a response from the original poster.

If the response works to solve your problem—great! But if you’re still having problems, do not let the issue’s closing deter you if you have additional questions! Post another comment and I will reopen the issue. Thanks!

@zachleat zachleat removed the waiting-to-close Issue that is probably resolved, waiting on OP confirmation. label Dec 19, 2018
@jtlapp
Copy link
Author

jtlapp commented Dec 20, 2018

It looks like I found a solution that doesn't require site generator support.

Would you mind sharing your solution?

@pamtbaau, it's just the one I previously described. Set a global variable to either a random number or the Unix time, and then append that number to each resource URL. E.g. site.js?{{unixTime}}.

@jtlapp
Copy link
Author

jtlapp commented Dec 20, 2018

Thank you @zachleat and @pamtbaau for pointing me to other possible solutions. Unfortunately, I'm having to put this project on hold for the moment, but will resume as soon as I can. I've decided to base my rewrite on Eleventy. Thanks so much for the generous assistance!

@dephiros
Copy link

Late to the discussion but I just did a quick solution using filter(inspired by hugo asset pipe), can probably extend to calculate hash

  eleventyConfig.addFilter(
    "tsUrl",
    /**
     * @param {string} url
     */ function(url) {
      const [urlPart, paramPart] = url.split("?");
      const params = new URLSearchParams(paramPart || "");
      params.set("ts", `${ts}`);
      console.log(params);
      return `${urlPart}?${params}`;
    }
  );

uses

{{ 'index.js' | url | tsUrl }}

@brycewray
Copy link

brycewray commented Nov 11, 2020

In the case that the only static assets you need to cache are CSS files (e.g., if you’re letting an external service handle your image assets) and you’re using PostCSS, there’s a PostCSS plugin called PostCSS Hash that makes it ultra-simple. I wrote about it here.

Later edit: Turned out there were flaws with that approach, so please see also this follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants