How to Use ETag for Caching in REST APIs
Use the
ETag HTTP header to provide a unique identifier for a resource version. Clients send this ETag back in the If-None-Match header to check if the resource has changed, enabling the server to respond with 304 Not Modified if it hasn't, saving bandwidth.Syntax
The ETag header is sent by the server with the resource response. The client includes this value in the If-None-Match header in subsequent requests to check if the resource has changed.
- ETag: A unique string representing the resource version.
- If-None-Match: Sent by client with the previously received ETag.
- 304 Not Modified: Server response when resource is unchanged.
http
HTTP/1.1 200 OK ETag: "12345abcdef" Content-Type: application/json { "data": "resource content" } --- Client Request --- GET /resource HTTP/1.1 If-None-Match: "12345abcdef" --- Server Response if unchanged --- HTTP/1.1 304 Not Modified
Example
This example shows a simple REST API in Node.js using Express that sets an ETag header and handles conditional requests with If-None-Match to return 304 Not Modified when appropriate.
javascript
import express from 'express'; const app = express(); const resource = { data: 'Hello, world!' }; const etagValue = 'W/"123456789"'; // Weak ETag example app.get('/resource', (req, res) => { const clientEtag = req.headers['if-none-match']; if (clientEtag === etagValue) { res.status(304).end(); // Resource not changed } else { res.set('ETag', etagValue); res.json(resource); } }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
Output
Server running on http://localhost:3000
Common Pitfalls
- Not generating a proper unique
ETagvalue that changes when the resource changes. - Ignoring weak vs strong ETags; weak ETags allow minor changes, strong require exact match.
- Failing to handle
If-None-Matchheader correctly, causing unnecessary data transfer. - Not setting
ETagheader on all responses for cacheable resources.
javascript
/* Wrong: Static ETag that never changes */ res.set('ETag', 'static-etag'); /* Right: Generate ETag based on resource content or version */ const etag = generateEtag(resourceData); res.set('ETag', etag);
Quick Reference
Use this quick guide to remember how to implement ETag caching:
| Header | Purpose | Example |
|---|---|---|
| ETag | Server sends unique resource version | "W/\"123456789\"" |
| If-None-Match | Client sends ETag to check freshness | "W/\"123456789\"" |
| 304 Not Modified | Server response if resource unchanged | Status code only, no body |
Key Takeaways
Always generate a unique ETag that changes when the resource changes.
Clients use the If-None-Match header to send the ETag back for validation.
Respond with 304 Not Modified to save bandwidth when resource is unchanged.
Use weak or strong ETags appropriately based on your caching needs.
Set ETag headers on all cacheable responses to enable efficient caching.