How do I generate an ETag HTTP header for a resource file?

+4  A: 

An etag is an arbitrary string that the server sends to the client that the client will send back to the server the next time the file is requested.

The etag should be computable on the server based on the file. Sort of like a checksum, but you might not want to checksum every file sending it out.

server client

I built up a string in the format "file inode number/datestamp/file size". So, if a file is changed on the server after it has been served out to the client, the newly regenerated etag won't match if the client re-requests it.

char *mketag(char *s, struct stat *sb)
    sprintf(s, "%d/%d/%d", sb->st_ino, sb->st_mtime, sb->st_size);
    return s;
Mark Harrison
If mtime is the time the file was last changed, then what's the purpose of size and inode?
In my case, it's because it was a computed path from a CGI program. You're right that in the case of a direct path that the mtime would probably be sufficient. Since the cost is mainly going to be in stat(), there's no extra charge for including the inode and size, which might protect from the (quite unlikely, of course) case where a rogue admin might update a file and touch it back to the original mtime.
Mark Harrison
+3  A: 


By default, both Apache and IIS embed data in the ETag that dramatically reduces the odds of the validity test succeeding on web sites with multiple servers.


If you're not taking advantage of the flexible validation model that ETags provide, it's better to just remove the ETag altogether.

+6  A: 

As long as it changes whenever the resource representation changes, how you produce it is completely up to you.

You should try to produce it in a way that additionally:

  1. doesn't require you to re-compute it on each conditional GET, and
  2. doesn't change if the resource content hasn't changed

Using hashes of content can cause you to fail at #1 if you don't store the computed hashes along with the files.

Using inode numbers can cause you to fail at #2 if you rearrange your filesystem or you serve content from multiple servers.

One mechanism that can work is to use something entirely content dependent such as a SHA-1 hash or a version string, computed and stored once whenever your resource content changes.

Justin Sheehy

I would recommend not using them and going for last-modified headers instead.

Askapache has a useful article on this. (as they do pretty much everything it seems!)

Rich Bradshaw
askapache link is broken
Hmm, that's a shame, hope they come back up soon as the site was a goldmine of advice!
Rich Bradshaw
The link is back up now.
Marius Gedminas