0%

Fix Different ETags for the Same Static File Across Nginx Nodes

I was recently using Nginx to serve some static files as configuration payloads. Since Nginx has supported ETags by default since version 1.3.3, it is convenient to let browsers fetch a new file automatically when the configuration changes.

But after deploying to a test environment, I ran into a problem. There were two Nginx nodes behind a load balancer, and the same static file returned different ETags depending on which node handled the request. As a result, the browser kept receiving 200 responses instead of 304, so caching was effectively broken.

After looking into how Nginx calculates ETags, I found that Nginx uses the file size and the file modification time. The ngx_http_set_etag function in the Nginx source contains code like this:

1
2
3
4
5
6
etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
r->headers_out.last_modified_time,
r->headers_out.content_length_n)
- etag->value.data;

r->headers_out.etag = etag;

Full source reference:

https://github.com/nginx/nginx/blob/1f01183b9e6658749934313fd72f7f16c1918b54/src/http/ngx_http_core_module.c#L1673

That makes it pretty clear: if two Nginx nodes return different ETags, the file modification time is probably different on each node.

Fortunately, changing file modification times is easy. You can use touch. The -t option is documented like this:

-t STAMP
use [[CC]YY]MMDDhhmm[.ss] instead of current time

So on both nodes, the following command sets the file timestamp to 2022-02-25 13:13:

1
touch -t 202202251313 myconf.json

If you want to do the same thing in Python, you can use os.utime:

1
2
import os
os.utime('myconf.json', (1645806368, 1645806368))

After doing that, both Nginx nodes returned the same ETag, and the second request correctly came back as 304 instead of 200.

Reference:

如果我的文字帮到了您,那么可不可以请我喝罐可乐?