If-Modified-Since header: format, example and nginx's exact-match default (tested)
If-Modified-Since sends back a date; if the file hasn't changed since then, the server answers 304 Not Modified with no body. We tested which dates nginx accepts.
1. Format and example
If-Modified-Since: Thu, 01 Oct 2026 10:00:00 GMT
$ curl -sI -H 'If-Modified-Since: Thu, 01 Oct 2026 10:00:00 GMT' https://site/a.txt
HTTP/1.1 304 Not Modified
$ curl -sI -z 'Thu, 01 Oct 2026 10:00:00 GMT' https://site/a.txt # curl's shortcut
HTTP/1.1 304 Not Modified
Always GMT, in the same format as the server's Last-Modified. Browsers simply echo the Last-Modified value they received, which is the safest choice.
2. What nginx answered (file Last-Modified 10:00:00 GMT)
| If-Modified-Since | default (exact) | if_modified_since before; |
|---|---|---|
| 10:00:00 (same as Last-Modified) | 304 | 304 |
| 11:00:00 (later) | 200 | 304 |
| 09:00:00 (earlier: file changed since) | 200 | 200 |
Thursday, 01-Oct-26 10:00:00 GMT (old RFC 850 form) | 304 | 304 |
garbage | 200 | 200 |
The surprise is row 2: by default nginx only returns 304 when the date matches Last-Modified exactly. A client that sends "now" or its own download time gets the full file every time. Either send back the server's Last-Modified verbatim, or set if_modified_since before; in nginx to get the textbook "not modified since" behaviour.
3. ETag wins
With the exact matching date and If-None-Match: "x" (a wrong ETag), nginx answered 200: when both are sent, the ETag check decides. Clients that store both should send both; servers use the ETag (how nginx builds ETags).
4. When it is used
Browsers send If-Modified-Since when revalidating a cached file that has Cache-Control: no-cache, an expired max-age, or no caching headers. Which Cache-Control values trigger a revalidation was measured separately.
Frequently asked questions
What is the format of If-Modified-Since?
An HTTP date in GMT, e.g. Thu, 01 Oct 2026 10:00:00 GMT. nginx also accepted the old form Thursday, 01-Oct-26 10:00:00 GMT; an invalid date got a normal 200.
Why does nginx return 200 instead of 304 for If-Modified-Since?
nginx's default if_modified_since exact only answers 304 when the date equals Last-Modified. A later date got 200 in our test; 'if_modified_since before;' changes that.
If-Modified-Since or If-None-Match?
If both are sent, the ETag decides: a wrong If-None-Match gave 200 although the date matched.
How do I send If-Modified-Since with curl?
curl -H 'If-Modified-Since: <date>' URL, or curl -z '<date>' URL. Both got 304 for the exact Last-Modified date.
Written with AI assistance. Measured on 2026-10-01 with curl 8.14.1 against nginx 1.31.6 (nginx:alpine, throwaway container) serving a file whose Last-Modified was Thu, 01 Oct 2026 10:00:00 GMT, with the default if_modified_since setting and with 'if_modified_since before'. When browsers send the header (section 4) is from the HTTP spec, not this test. PlainHeaders is our extension; curl is free.