It is common practice to separate front end and back end service for a website. One of the reasons for this is so that we can scale back end or front end separately. Most of the time, we need more resources to serve back end than the front end. To reduce loads that coming to backend we need some caching. Fortunately, there is a Cache-Control header. The back end can control how the front end should cache the resource by using this header. Cache-control is commonly used for image files or static files such as javascript. It also can be used to ajax JSON request or any other request. This article will show you how to use the Cache-Control header to reduce load to back end for your website.
What is Cache-Control
Cache-Control is an HTTP header that tells the client how is the caching policy of a resource. An example of the value is max-age=3600
. It means that the browser should cache the resource for 3600 seconds or one hour. If the next request for the same resource is still in one hour range, the browser should not hit backend, and use the cached response instead. It also means that for one hour the back end doesn’t need to handle requests for the resource because it is cached at the front end.
Q: What will happen after the max-age is expired?
A: Front end will send a new request to the back end to get a newer response.
Q: But, if there is no update to the resource, can we cache it further?
A: Yes, the browser can ask to back end if there is any update to the resource. If there is no update, the browser can use a cached resource even if the max-age is expired.
Q: How?
A: By usingetag
header.
Etag is an HTTP header that tells us the version of the resource. When the browser got etag in the response header, it will use it in If-None-Match
header value in the next request. Back end will check if If-None-Match
header is the same with current etag. If it is the same, back end response with HTTP code 304
with an empty body. Then the front end will use the previously cached resource. This also reduces back end load because it doesn’t need to fetch the resource from database.
If a resource is updated, the back end should update its etag value. So the front end knows that there is an update and fetch updated resource.
Utilize Cache-Control to reduce load to backend
Our service may have some resource that is rarely updated and some resource that is frequently updated. Cache-Control can be used for both use cases. For rarely updated resources, you can put long max-age
in the Cache-control header. You may want to use etag
to specify the version of the resources.
Example in golang:
var menuVersion = 1
var cacheExpirySeconds = 3600
func HandleGetMenu(w http.ResponseWriter, r *http.Request) {
if match := r.Header.Get("If-None-Match"); match != "" && match == menuVersion {
w.WriteHeader(http.StatusNotModified) // http code: 304
return
}
menu := fetchMenu()
w.Header().Set("Etag", fmt.Sprint(menuVersion))
w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d", cacheExpirySeconds)
w.Write(menu)
}
In this example, I use hardcoded menuVersion as etag. In a real application, you may need to store it in some database. Line 4-7 checks if the server menu’s version matches the version that the client sent. If it matches, we just respond with HTTP code 304 without any response body. The back end server doesn’t need to call fetch menu and the client will use previously cached respond.
For frequently updated resource, you can use max-age=0
in Cache-Control header with a version in etag
. It means, the client will always hit back end, but if the version is a match you just need to respond 304 without having to fetch data from the database.
Conclusion
I think Cache-Control and etag headers are very useful because the back end can control how the client should cache the response. And all major browsers support it. If used correctly, you can reduce load to your back end significantly. But if you configure it wrong you may need to tell user to clear the browser cache. If you have any questions or have another idea, please put your comment below.