🔄 Understanding ETag and If-None-Match in Web Development
Abhinav

Abhinav @abhivyaktii

About: Learning In Public

Location:
Bengaluru, India
Joined:
Nov 10, 2021

🔄 Understanding ETag and If-None-Match in Web Development

Publish Date: May 21
0 0

🔄 Understanding ETag and If-None-Match in Web Development

In the world of web development, optimizing resource delivery isn’t just about CDN and lazy loading. Headers like ETag and If-None-Match are low-key heroes 🦸‍♂️ when it comes to reducing bandwidth and improving load speed 🚀.

In this post, I’ll walk through:

  • 🧠 What ETags and If-None-Match actually are
  • 🤝 Why you should care
  • 🔁 How the browser and server interact with these headers
  • 🛠️ Real-world usage in a NestJS backend

Let’s dive in 🏊‍♂️


🧠 What is an ETag?

An ETag (short for Entity Tag) is like a fingerprint 🧬 for a resource. Every time your backend returns something (a JSON response, image, HTML, etc.), it can attach an ETag header — a unique string that represents the current version of that resource.

Example server response:

200 OK
ETag: "abc123"
Enter fullscreen mode Exit fullscreen mode

This value is cached by the client 📦. On future requests, the client can ask:

“Hey server, is this still version abc123?”

If yes, the server replies:

304 Not Modified
Enter fullscreen mode Exit fullscreen mode

…with no response body — saving bandwidth 💡.


📨 What is If-None-Match?

If-None-Match is the request header the client sends to say:

“Only send the data if it’s different from the version I already have.”

Example:

GET /api/user/123
If-None-Match: "abc123"
Enter fullscreen mode Exit fullscreen mode

If the server's version is still the same, it responds with:

304 Not Modified
Enter fullscreen mode Exit fullscreen mode

Otherwise, it sends the updated content with a new ETag 🆕.


🔁 How They Work Together

Here’s the flow:

  1. 🧑‍💻 Client requests a resource
  2. 🧾 Server responds with data + ETag: "xyz789"
  3. 💽 Client saves the response + ETag
  4. 🔄 Next time, client sends If-None-Match: "xyz789"
  5. ✅ If match → server replies with 304 Not Modified
  6. ❌ If not → updated data is sent with a new ETag

It’s like conditional rendering 🔃, but for APIs.


⚡ Why You Should Care (The Benefits)

  • 🪄 Save Bandwidth – Don’t send the same thing twice
  • 🚀 Faster UX – Cached responses mean quicker loads
  • 🔐 Safe Concurrency – Prevent stale updates with version checks

🛠️ Implementing ETags in NestJS (with Code)

Let’s say you have a user API and want to skip re-sending the same user data when it hasn’t changed.

1. Generate an ETag from your response

// etag.util.ts
import * as crypto from 'crypto';

export function generateEtag(data: any): string {
  const str = JSON.stringify(data);
  return crypto.createHash('sha1').update(str).digest('hex');
}
Enter fullscreen mode Exit fullscreen mode

2. Use ETag and If-None-Match in your controller

import { Controller, Get, Param, Req, Res, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';
import { generateEtag } from './etag.util';

@Controller('user')
export class UserController {
  @Get(':id')
  async getUser(@Param('id') id: string, @Req() req: Request, @Res() res: Response) {
    const user = { id, name: 'Alice' }; // Dummy user data

    const etag = generateEtag(user);
    const clientEtag = req.headers['if-none-match'];

    if (clientEtag === etag) {
      return res.status(HttpStatus.NOT_MODIFIED).setHeader('ETag', etag).end();
    }

    res.setHeader('ETag', etag);
    return res.status(HttpStatus.OK).json(user);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you’re only sending data when necessary. Simple and effective 💯.


🧪 Practical Example

Let’s say your site has a giant CSS file 🎨:

  1. 👀 First visit → browser downloads it, gets ETag: "v1"
  2. 👋 Second visit → browser sends If-None-Match: "v1"
  3. 🔁 If unchanged → server responds with 304, no download

Result? Less waiting. Better UX. Happy user 😊.


⚠️ Considerations

  • ⚙️ ETag Generation: Needs to be deterministic. Use stable hashing.
  • 🧵 Weak ETags: W/"etag" means “close enough” (used for minor changes).
  • 🧩 Middleware/CDNs: Some proxies or CDNs may strip or override ETags.
  • ⏱️ Overhead: For dynamic content, ETag hashing might add slight load.

🧷 Bonus: Updating Resources Safely with If-Match

Imagine two users editing the same record 🧑‍🤝‍🧑. You can use If-Match to prevent overwriting someone else’s changes.

PUT /user/123
If-Match: "v1"
Enter fullscreen mode Exit fullscreen mode

NestJS:

@Put(':id')
async updateUser(@Param('id') id: string, @Req() req: Request, @Res() res: Response) {
  const currentUser = { id, name: 'Alice' };
  const currentEtag = generateEtag(currentUser);
  const clientEtag = req.headers['if-match'];

  if (clientEtag !== currentEtag) {
    return res.status(HttpStatus.PRECONDITION_FAILED).send('ETag mismatch');
  }

  // Perform the update
  return res.json({ message: 'Updated successfully' });
}
Enter fullscreen mode Exit fullscreen mode

It’s like optimistic locking, but way easier 🔐.


📥 Recap of Request & Response Headers

📤 Request Headers:

If-None-Match: "etag-value"  // For GET
If-Match: "etag-value"       // For PUT/UPDATE
Enter fullscreen mode Exit fullscreen mode

📥 Response Headers:

ETag: "etag-value"
Enter fullscreen mode Exit fullscreen mode

✅ TL;DR

Header Direction Who Sends It Used For
ETag Server ➡️ Client Server Identifying resource version
If-None-Match Client ➡️ Server Client Conditional GET (cache check)
If-Match Client ➡️ Server Client Safe updates
304 Not Modified Server ➡️ Client Server Says: “You already have it”
412 Precondition Failed Server ➡️ Client Server Update blocked due to version mismatch

🧠 Final Thoughts

ETag and If-None-Match are underrated performance boosts 🚀. They save bandwidth, speed up your app, and help avoid update collisions — all with just a few headers.

And with NestJS, it’s super easy to implement 💪.

Comments 0 total

    Add comment