According to clause No. 4.3 of RFC 7234:
Thus, I expect caddy to cache the first response and subsequently validate cached data via a conditional request. However, caddy simply forwards a request to an upstream without any conditions.
$ curl -ik localhost:80
HTTP/1.1 200 OK
Cache-Control: no-cache
Cache-Status: Souin; fwd=uri-miss; stored; key=GET-http-localhost-/
Last-Modified: Mon, 15 Dec 2025 08:52:20 GMT
...
$ curl -ik localhost:80
HTTP/1.1 200 OK
Cache-Control: no-cache
Cache-Status: Souin; fwd=request; fwd-status=200; key=GET-http-localhost-/; detail=REQUEST-REVALIDATION
Last-Modified: Mon, 15 Dec 2025 08:52:20 GMT
...
{"level":"info","ts":1766736377.4338582,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"127.0.0.1","remote_port":"48850","client_ip":"127.0.0.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip"],"Accept":["*/*"],"Date":["Fri, 26 Dec 2025 08:06:17 UTC"],"Via":["1.1 Caddy"],"X-Forwarded-Host":["localhost"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["172.16.249.1"]}},"bytes_read":0,"user_id":"","duration":0.000199667,"size":11,"status":200,"resp_headers":{"Content-Type":["text/plain; charset=utf-8"],"Server":["Caddy"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Cache-Control":["no-cache"]}}
{"level":"info","ts":1766736377.4560177,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"172.16.249.1","remote_port":"57879","client_ip":"172.16.249.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"User-Agent":["curl/8.7.1"],"Accept":["*/*"]}},"bytes_read":0,"user_id":"","duration":0.023146958,"size":11,"status":200,"resp_headers":{"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Server":["Caddy"],"Content-Length":["11"],"Via":["1.1 Caddy"],"Date":["Fri, 26 Dec 2025 08:06:17 GMT"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"],"Cache-Status":["Souin; fwd=uri-miss; stored; key=GET-http-localhost-/"]}}
{"level":"info","ts":1766736406.6892934,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"127.0.0.1","remote_port":"48850","client_ip":"127.0.0.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"X-Forwarded-For":["172.16.249.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["*/*"],"Date":["Fri, 26 Dec 2025 08:06:46 UTC"],"Via":["1.1 Caddy"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"]}},"bytes_read":0,"user_id":"","duration":0.000023417,"size":11,"status":200,"resp_headers":{"Server":["Caddy"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"]}}
{"level":"info","ts":1766736406.6932402,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"172.16.249.1","remote_port":"57892","client_ip":"172.16.249.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"Accept":["*/*"],"User-Agent":["curl/8.7.1"]}},"bytes_read":0,"user_id":"","duration":0.019553791,"size":11,"status":200,"resp_headers":{"Content-Length":["11"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Via":["1.1 Caddy"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"],"Cache-Status":["Souin; fwd=request; fwd-status=200; key=GET-http-localhost-/; detail=REQUEST-REVALIDATION"],"Server":["Caddy"],"Date":["Fri, 26 Dec 2025 08:06:46 GMT"]}}
As we may see, the request made by caddy is not conditional.
According to clause No. 4.3 of RFC 7234:
Thus, I expect caddy to cache the first response and subsequently validate cached data via a conditional request. However, caddy simply forwards a request to an upstream without any conditions.
I've tested it only with
Last-Modifiedandno-cache, because of the bug I noticed withETag. Additionally, I couldn't trigger revalidation using other directives likemust-revalidateandstale-while-revalidate.Reproduction steps:
{ log cache { stale 60s } } http://localhost:80 { log cache reverse_proxy http://localhost:81 } http://localhost:81 { log header Last-Modified "Mon, 15 Dec 2025 08:52:20 GMT" header Cache-Control "no-cache" respond "Hello world" }logs:
{"level":"info","ts":1766736377.4338582,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"127.0.0.1","remote_port":"48850","client_ip":"127.0.0.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip"],"Accept":["*/*"],"Date":["Fri, 26 Dec 2025 08:06:17 UTC"],"Via":["1.1 Caddy"],"X-Forwarded-Host":["localhost"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["172.16.249.1"]}},"bytes_read":0,"user_id":"","duration":0.000199667,"size":11,"status":200,"resp_headers":{"Content-Type":["text/plain; charset=utf-8"],"Server":["Caddy"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Cache-Control":["no-cache"]}} {"level":"info","ts":1766736377.4560177,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"172.16.249.1","remote_port":"57879","client_ip":"172.16.249.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"User-Agent":["curl/8.7.1"],"Accept":["*/*"]}},"bytes_read":0,"user_id":"","duration":0.023146958,"size":11,"status":200,"resp_headers":{"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Server":["Caddy"],"Content-Length":["11"],"Via":["1.1 Caddy"],"Date":["Fri, 26 Dec 2025 08:06:17 GMT"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"],"Cache-Status":["Souin; fwd=uri-miss; stored; key=GET-http-localhost-/"]}} {"level":"info","ts":1766736406.6892934,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"127.0.0.1","remote_port":"48850","client_ip":"127.0.0.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"X-Forwarded-For":["172.16.249.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["*/*"],"Date":["Fri, 26 Dec 2025 08:06:46 UTC"],"Via":["1.1 Caddy"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"]}},"bytes_read":0,"user_id":"","duration":0.000023417,"size":11,"status":200,"resp_headers":{"Server":["Caddy"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"]}} {"level":"info","ts":1766736406.6932402,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"172.16.249.1","remote_port":"57892","client_ip":"172.16.249.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/","headers":{"Accept":["*/*"],"User-Agent":["curl/8.7.1"]}},"bytes_read":0,"user_id":"","duration":0.019553791,"size":11,"status":200,"resp_headers":{"Content-Length":["11"],"Last-Modified":["Mon, 15 Dec 2025 08:52:20 GMT"],"Via":["1.1 Caddy"],"Cache-Control":["no-cache"],"Content-Type":["text/plain; charset=utf-8"],"Cache-Status":["Souin; fwd=request; fwd-status=200; key=GET-http-localhost-/; detail=REQUEST-REVALIDATION"],"Server":["Caddy"],"Date":["Fri, 26 Dec 2025 08:06:46 GMT"]}}As we may see, the request made by caddy is not conditional.