What is ETag and how to use it in Rails for caching?
Every now and then, in the realm of web development, we come across terms that are crucial to the operation but may seem obscure until we delve into them. An ETag, or entity tag, is one such term. In this article, we will explore what ETags are, why they are important, and how to use them.
Defining ETags
An ETag is, simply put, a part of the HTTP protocol designed for optimistic concurrency control. They act as a mechanism for detecting content changes at a given URL. Whenever a URL is accessed, the server returns an ETag, a unique identifier. If the content changes, the ETag changes as well, signaling to clients that a new version of the resource is available.
# curl -i 'https://jsonplaceholder.typicode.com/todos/1'
HTTP/2 200
content-type: application/json; charset=utf-8
content-length: 83
etag: W/"53-hfEnumeNh6YirfjyjaujcOPPT+s"
via: 1.1 vegur
cf-cache-status: HIT
age: 14038
accept-ranges: bytes
server: cloudflare
cf-ray: 81fba87a9f0d35d2-WAW
alt-svc: h3=":443"; ma=86400
Why are ETags important?
ETags are important because they enable efficient updating of cached resources. They act as validators, checking whether or not the cache needs to be updated. If the ETag hasn't changed, the client can use the cached resource version instead, optimizing network usage and improving speed.
ETags result
Most web servers and client libraries support ETags out of the box, but the implementation of the ETag logic is left up to the developer. ETags can be used in conjunction with cache control headers to manage caching policies. For example, when Rails receives the same ETag in the header and compares it to the current stored version, it will return 304 Not Modified
to avoid re-calculating and re-rendering things.
Using expires_in
The expires_in
method is used to set the maximum age for the resource. It helps to cache a time-invariant response (one that does not change often). Here's how to use it:
def show
@post = Post.find(params[:id])
expires_in 10.minutes, public: true
end
In this example, the expires_in
method sets the max-age
value of the cache control header to 10 minutes.
Using expires_now
If you want to prevent a resource from being cached, you can use the expires_now
function. It sets the cache-control
header to no-cache
, ensuring that the browser and intermediate caches do not cache the page.
def show
@profile = Profile.find(params[:id])
expires_now if @profile.private?
end
Here, the expires_now
method is used to force the expiration of a resource when the profile is private.
Using stale?
The stale?
method helps set the ETag and Last-Modified
headers in the response and checks whether the client's copy of the resource is stale or fresh.
def show
@post = Post.find(params[:id])
if stale?(etag: @post, last_modified: @post.updated_at)
render json: @post
end
end
In this example, if the client request is stale, Rails will continue to render the @post
in JSON format.
Using fresh_when
The fresh_when
method helps take advantage of HTTP's conditional GETs. By comparing the current request's ETag and Last-Modified
headers to those previously stored, it helps you determine if the request is fresh.
def show
@posts = Post.all
fresh_when last_modified: @posts.maximum(:updated_at)
end
Here, fresh_when
checks if the @posts
is fresh based on its etag
value or updated_at
timestamp.
Declaring public requests
Public responses can be stored by Internet-based caching mechanisms and don't contain sensitive data. You can use public: true
to declare this type of response.
def show
@post = Post.find(params[:id])
fresh_when @post, public: true
end
Using http_cache_forever
The http_cache_forever
method is used when a page will never change. It has the same effect as setting expires_in
to a future value and ignoring all incoming invalidation requests.
def show
@post = Post.find(params[:id])
http_cache_forever(public: true) do
render json: @post
end
end
In this code, http_cache_forever
helps to cache this page forever because the response does not change. This means that every time the same request is made, the cached JSON representation is returned instead of recomputed, improving performance.
Conclusion
ETags play a critical role in improving Web performance by efficiently utilizing caching resources. As developers, understanding ETags can allow us to optimize our applications and APIs to significantly improve the end-user experience. Please remember to add caching only when it's necessary - not before building the stuff to save your time.
Happy coding!