Monday, Monday, Monday, open wide dev fans, get ready to stuff your face with JavaScript, CSS, Node module, barbecue tips, Git workflows, breakdancing, soft skills, web development, the hastiest, the craziest, the tastiest, web development treats, coming in hot, here is Wes, Barracuda, Moss, and Scott, El Toro, Loco, Tolinski. Welcome to Syntax on this Monday Hasty Treat, the 464th episode of Syntax, we're going to be talking about cache control headers. Now this is something that we're going to have to be spending more time on as web developers as tools start to utilize browser fundamentals and things like that, just a little bit more, right? I think we're seeing this with tools like Remix, I know SvelteKit makes this nice and easy, I'm sure other tools do as well.
So this all started off with a question from Chris M, but before we get into that, let's take a minute to talk about our sponsors and introduce Wes Boss, as always, who is the co-host of the show, Wes, coming live from Canada, how's it going? It's going good, let's talk about our sponsor, which is Sentry, Sentry does error exception and performance tracking, it's just an awesome company for, I specifically use to track all the bugs that I have on all my own website, so I, we just recently launched a whole big update, both to the back end, as well as to the big, the front end viewing experience, and with that, as soon as we launched that sucker, we had the Sentry issue page open, and we were just sort of eyes peeled, watching exactly what was happening, and you start to see these issues pop up, you fix them, you say they are fixed, and you hope they do not come back, you can, you can tie it to a specific version of your code, which is good, because like, one thing is, if you have an error that keeps popping up, and you fix it, but somebody hasn't refreshed the page yet, the error still comes, so you could say, all right, well, this is fixed in version 4.2 of our software, so ignore it, right? They just do so much, they hold performance, user feedback, you name it, they got it, check it out, Sentry.io, make sure you use the coupon code, TastyTreat, gets you two months for free, thank you, Sentry, for sponsoring. This episode is also sponsored by LogRocket, and LogRocket.com forward slash syntax is the perfect place to see all of your errors and exceptions happen in a visual scrubbable replay, but not just that, it allows you to really understand user behavior on your site.
Now, these are some things that I think have been somewhat recently added to LogRocket, these tools to really gain more insight into conversion rates, surface UX issues, so not only UI and code bugs, but also UX issues, easily build custom funnels, based on events for clicks, views, checkouts, and custom events, and see how your users journey through your site, engage, retain your customers with LogRocket, in addition to seeing all of your bugs. So check it out at LogRocket.com forward slash syntax, and you'll get 14 days for free. Thank you so much for LogRocket for sponsoring. Okay, so let's get into it.
This actually came to us from a really good potluck question, sometimes that happens when we'll get a potluck question that's so good, we're like, man, we could spend a whole show talking about this. So this question comes from Chris M, and this is, hey, Scott and Wes, love listening to the show overnight while I'm working in retail, and one of the things I've been working on lately with my website has been performance and utilizing things like Cloudflare workers to make the experience as fast as possible for my end users, as it's very image-heavy, think photo galleries. My question and struggle has been with caching, cache control headers, and would love a better explanation on some of the fields that I just blindly copy and paste from internet resources, and I just don't truly understand some of the things I put down, mean, or do. Help!
Exclamation point. So let's talk a little bit about cache control headers, caching in general, and how these things can help us build up more performant applications. Really, sometimes, you know, I think people, they spend so much time working on really custom solutions or working really hard on performance, reducing kilobytes here and there, and then they skip things like caching that could really even make a much bigger difference in much less effort. So, Wes, do you want to explain a little bit about what a header is, and then we can get into cache control headers?
So when you have either a request or a response, a request, it comes from you and the browser are sending a request to the server for an image, an HTML file, a list of dogs, you name it, and then what comes back from the server is a response. And both the request as well as the response can have additional pieces of metadata set along with the request or response. And that could be as simple as sending over an API key or sending over what type of response you're sending. Is it JSON or is it text?
You know, there's lots of metadata that goes along with the request or response. And one of those headers that comes along with the request or response is the cache control headers, which tells either the browser or the server how, or specifically, how fresh or stale the content is and if it should regenerate or fetch new content before it goes ahead. And let's get into the cache control headers. So there's kind of a couple different versions of caching.
And maybe we should explain what caching is just in a second. So caching means that you are saving the result of something that has been generated, whether it is an image, whether it's an HTML, whether it is a list of dogs, all of those things take work, whether that work is that the server needs to go to the database and get it, whether that work is your phone needed to download a huge image of a dog, all that work doesn't necessarily have to be replicated should you want that resource a second time. And that's what caching is, is that we will put it in a bag somewhere so that we can retrieve it from that bag in the future if it is still fresh. And there's a couple different areas of cache control.
There is a browser level cache. So you have an image of a dog and then you go back to that page a minute later, you have an image of a dog again, hokey, do you have to download that twice? No, your browser will cache that for you. There's also a CDN level.
So you go to your, let's say you want a list of dogs, you hit your server for endpoint forward slash dogs.json. You're the first time your server is going to generate that and go ahead and maybe make a list of dogs or maybe an HTML file. But the second time, maybe it doesn't make sense to regenerate all that. That can be stored at a CDN level.
And that's generally referred to as a public cache because that's like that stuff everybody can have. Whereas there's other, there's a private cache, which is generally your browser level. And then finally, there's like a reverse proxy that can sort of sit in front of your server as well. I mean, we cache a lot of our server stuff automatically in Redis, which is like an in-memory cache.
So, you know, even in that degree, like caching, like you mentioned, is really just saving things for later if they're not changed to make that operation faster in the future. And do you use the cache control to specify whether they should be cached in Redis or is that a different approach? So we do, well, we use a plugin for it. So I honestly don't know how it works under the hood.
In fact, we just give it a time to live. So that's actually like a big thing in caching is you'll often see TTL, which means time to live. How long should this thing live before we regenerate it and grab a new version of it and spend the resources to get to that new version of it? So TTL, time to live.
And time to live is in seconds, which can feel kind of obnoxious sometimes, right? We have a lot of seconds you're having to calculate. So actually, I'll link this in the show notes. I made a quick little time to live calculator in the spell repl that you could just like type in, oh, I want to cache this for five days or two months or whatever, and it just gives you the amount of seconds.
And so I'll make sure that's linked up. So benefits of a cache. A, it reduces the load on your server. B, it reduces bandwidth use, like we talked about.
And then C, probably the biggest one is the client speed, meaning that if you visited a website before, there are resources on that website that don't necessarily have to be redownloaded, reparsed, and all that kind of stuff. So making everything as fast as possible is sort of the benefit of the cache, as well as, at the end of the day, it can save you money, and it can save your users money who are paying for the bandwidth bill and all that kind of stuff. It can also increase those user misery scores on Sentry, right? Oh, yeah, there you go.
It can make your site feel way faster for your users, which is honestly the biggest lesson in my mind, is that perceived time and actual real time of loading your site will be better for those users. Exactly. So the question we have now is, how do you specify how long things are cached? And luckily, there are standards for that.
These are through things called directives. And directives are essentially, so that the header is called cache control, and a directive is essentially how you want to describe how long the cache should be, or when it should, when it is stale, or if it is immutable, we'll talk about that. So there are cache control response directives. That means they are set on your server and sent to the client, sent to the browser.
And then there are cache control request directives. I'm going to say that most of your cache control stuff will happen from the server to the client. However, you can override those or do it the other way from the client to the server. So let's go through each of those now.
You want to grab the first one? Yeah. You know, to be honest, I don't know that much about these. So I mean, I know a little bit, but the first one is max age, which max age is basically a time to live.
Like, how long is this thing going to live, right? So the max age is the one that I use primarily because it's easy to use in spell kit. You just say cache max age. So max age is basically how long should this thing live before we grab a new one?
And so what do I have here? I have one that has a max age of 86,400. Hold on, however long that is. 86,000.
Hold on, let's do the math. So divided by 60, that is 1,400 minutes. Divided by 60, that's 24 hours. There you go.
So that is a one day. Exactly. I came up with that for my spell calculator. It's nice that it's done in seconds, not milliseconds, because then you don't have to do that extra 1,000 millisecond division.
Essentially, how that works is that your browser will save all of your assets. Again, pictures of dogs, HTML images, you name it, and before it tries to request it first, it will look at the header on that item and say, all right, well, the max age of this thing is one day, and I only got it three hours ago. So therefore, I'm not going to go back to the server and ask for another copy of that. There also is no cache.
That will obviously not cache the thing at all or ever. That's very handy for things that are constantly changing. This is the big one that everybody's all excited about, and it's stale while revalidate. So what this would do?
Yeah, so what stale? This is the SWR. This is the whole idea behind the Next.js data fetching library, and this is a browser standard. Actually, sorry, I have right underneath it, non-standard.
Apparently, it's not standard, but everybody, every browser has implemented, except you know who, the zoo-themed library. So stale while revalidate will, essentially what that means is serve the stale version from the cache, but in the background, go and fetch the new version. So you have a website, you visited it, and you have a list of dogs, serve up the old list of the dogs, even if it is considered stale. So again, that whole max age thing, it is considered fresh before it, stale after.
So do that. But in the background, go ahead and fetch the new version. What I don't know, and maybe somebody on Twitter can answer this, tweet, as a text FM, is will the browser automatically swap out the asset for you on the page, or does it wait until the next refresh? I think it's the latter.
It's funny, because this is something that we all kind of have to do by hand right now, which I didn't know that this was a non-standard standard here. So that's interesting, yeah. I just saw a tweet from Kent C. Dodds.
They have RemixConf going on right now, and he's blowing his horn about how amazing the RemixConf website is. And he says, it's just using browser standards, and the screenshot of the headers, and what is powering that? Stale while revalidate, meaning that you can visit the website, you'll immediately get the stale version, and then you get the new version in the background. And that's actually how the syntax website works as well.
The syntax website will, when you visit it, you will get the stale version, meaning that it has been generated when somebody else visited it. We put that in the cache, and then we will serve you that up. But what happens is, in the background, we are generating the next version of the syntax website. And the reason that we do that is because when there is a new episode of syntax, it will just automatically show up the next time somebody visits the website, because it was regenerated.
So you're not visiting syntax.fm and waiting for the website to generate. You get the version from the last guy, and then the next guy gets your version, right? It's kind of off by one. But that allows it to be both fresh as well as pre-generated, if that makes sense.
Yeah. It seems like the ideal way to do things. And that's how we're doing a lot of caching these days. You know, whether that is, like, we've cached images on, you know, Cloudinary or whatever, where it's giving you, you know, the first person is doing all the work, and then the next people are getting the cached version.
So, but this way, it does all the work in the background. So even the first person is getting a previously cached version, and the next people are getting the next cached version. So you're always getting what could be considered, like, a performant version of the load. Next one we have here.
We're not going to go through them all, because you can obviously read through the list. We're just going through the ones that are probably the most common. Scale if error. So this one is not very well supported, but it's sort of, the whole idea, this is why libraries like Remix are becoming popular, is that these headers are going to take a lot of the load off and make it very easy to build performant websites without building something custom.
It's built into the browser, use the platform, right? So scale if error will just serve the scale version if the asset cannot be downloaded or there's an error that is downloading. The must-revalidate response directive indicates that the response can be stored in the caches and can be used while fresh. If the response becomes stale, it must be validated with the origin before it, meaning that you use that in conjunction with max age, and then you say, even if it is still fresh, you have to still check for the new version.
There's other ones here, private, that will say that this asset can only be cached on a private cache, and that to you is the browser. The browser is a private cache, whereas Cloudflare or CacheFly or any of these CDNs that sit in between your server and the client, those things will try to cache as much as they possibly can. And if you want to explicitly say, no, no, no, no, this is private data, or I never want this to be cached in the CDN, you can pop a private directive on there, and it will make sure that it is not thrown in there. That's beautiful.
You don't have to figure out some custom API. It's just standard. Immutable, this is kind of interesting. This one's specifically for assets, and I like to think of it as CSS and JavaScript, where if you have style.css, you pop an immutable tag on there, and what that will tell the browser is cached this thing essentially forever, because if you want to load a new version of style.css, you must change the name of the resource instead of changing the style.css file.
And that's kind of cool, because that makes people use style.1.1.2.css or scripts.1.2.3.js. Another way... I think it's important to call out that typically when you're working with many frameworks, if you look at the source of your framework, we take a look at the source of our framework, and we see that any of the bundled JavaScript or CSS that's loaded into that file can often have those hashed files where the file has all that. It's not just index.css.
It's index 800 characters and then .css. And that is why, right? Because if it is, you want it to be just straight up cached by the browser, and you want it to always have that one, and if the file changes, then that hash, that unique identifier for that will change, and therefore I will know to grab the next version of it. One kind of other one I saw here that was interesting is the no transform cache control header, and what that does is it tells an intermediary CDN not to mess with the original files.
So there are some CDNs, like Cloudflare is a big one. I don't know if they... Yeah, I just looked it up on their docs. They do have support for this.
So Cloudflare will try to compress your images. without actually changing them and if you want them to not mess with that at all then you can put the no transform on there i'm assuming this works for other things like cloudinary or opera mobile maybe opera mobile is a like a it's a browser but it's also a service that tries to crunch images and css and it tries to crunch everything first so that it can send it to you as fast as possible it's a proxy in the middle and if you specifically need to tell the cdn in the middle to not to bypass that you can pop a no transform header on that and if they respect those headers then they will not they'll just pass the raw image or the raw asset through to you interesting yeah so those are all response directives they're coming from the server to the client and then there's the other way around if you are on the client you need to specifically ask the server for a version of something uh you can pop a no cache header on there um a no store header on there or you can specifically overwrite the max age max sale and min fresh headers as well so um i could see that being handy if you had like a button that says like i don't know fetch dogs but like fetch fetch it from source and you wanted to have a special button that goes past the cache um that would be really handy you pop that header on there and that would be enough to tell your cdn or your server bypass any caches you possibly have regardless of headers um and give me give me the raw stuff give me the good stuff that's what i got there so uh thank you chris m for writing in this question i thought it was good enough to dedicate entire hasty towards i think we'll see a lot more of this it's not really all that complicated once you realize that there's just maybe three or four handfuls probably the stale while revalidate and the max age are probably the two biggest ones that you want to start using yeah and like we mentioned i mean caching can just be a huge performance boost and you might be spending a lot of time elsewhere trying to get your your code to be faster to run more performally for your users and at the end of the day if you're not implementing some of these basic caching mechanisms that are built into the web platform overall then you might be missing out on a huge opportunity to see some major perf gains you know what i always say cache rules everything around me yes i yeah yeah we used to joke about making t-shirts i said that in like 2011 yeah yeah because we're all wu-tang wu-tang heads i'm actually i wonder what your your um familiarity with wu-tang even is with mine yes what's your what's your wu-tang familiar i'm a little bit of a woo head yeah i wouldn't i wouldn't say a big fan but i'll throw it on sometimes yeah yeah sometimes i like to throw it on i'm not the fact that i would wear a wu-tang shirt um but i definitely will laugh at a cream t-shirt or sticker that somebody has on their laptop although i just googled it it seems like the geocaching community has sort of co-opted that joke oh geocachers what are they hiding things in the forest and trying to find them come on what a crazy hobby yeah i actually saw wu-tang once in a concert and it was awesome i saw them in new york and it was fantastic and then we got to uh be a b-boy battle that was at the wu-tang concert so we actually went for the b-boy battle um and we got to uh battle some guys from poland and they destroyed us it was uh it was uh very unfortunate because they're like internationally um competitive like these dudes are you know they compete on major major stages and we had to go against them in the first round and we're just like oh man we're gonna get worked and we got we got we got destroyed so and also like i wanted to go up to them and be like hey i'm a polish heritage but they only speak polish and i uh i speak no polish so i couldn't even be like listen i'm kind of polish uh let's be friends and i couldn't even do that it was embarrassing so here's a question uh i lay vichigander at heart uh would you rather go see wu-tang or icp oh my god wu-tang what yeah uh i have no no love i'm not down with the clowns i say i um it's funny i worked at an agency that got requested um a request for what do they call that like a request for the price request for proposal request for proposal from icp and they uh they actually wanted us to do their site um i kid you not they said yes we want this we want this agency to do their site and you know what we sat down as an agency and um the owner of the agency was basically like at the end of the day i don't really want to do this work and we chose to decline them as a client oh there's a really good documentary online if you just search like icp documentary pretty pretty crazy i was like the workaholics episode where they go to the clown festival or whatever it's called oh man all right that's it for today thanks everybody for tuning in and we will catch you on wednesday peace peace head on over to syntax.fm for a full archive of all of our shows and don't forget to subscribe in your podcast player or drop a review if you like this show