When using Souin as a Caddy plugin, the stale-while-revalidate (SWR) feature partially works: stale content is served correctly, but the background revalidation always fails with "context canceled". This happens because Caddy cancels the request context immediately after the main response is sent, before the background goroutine can complete the upstream request.
Environment
Souin version: Tested with PR feat(rfc): stale-while-revalidate #690 commits (5dfcbdde and 2e1f6692)
Caddy version: Latest via xcaddy
Go version: 1.23.5
Storage backend: Dragonfly (Redis-compatible)
Platform: Docker/Linux
Caddyfile Configuration
cache {
ttl 20s
stale 10s
default_cache_control "public, max-age=20, stale-while-revalidate=10"
timeout {
backend 60s
cache 60s
}
storers redis
redis {
configuration {
Addrs dragonfly:6379
DB 1
}
}
}
reverse_proxy backend:3000
Steps to Reproduce
Make initial request → Cache stores response (TTL=20s, stale=10s)
Wait 22 seconds (past TTL, within stale window)
Make second request with Cache-Control: max-stale=300
Expected Behavior
Stale response returned immediately ✅
Background revalidation fetches fresh content from upstream
Cache updated with fresh content
Actual Behavior
Stale response returned immediately ✅
Background revalidation fails instantly with "context canceled" ❌
Cache NOT updated; next request triggers another cache miss
Debug Logs
Cache-Status: Souin; hit; ttl=-3; key=GET-/--de-default; detail=REDIS
DEBUG http.handlers.cache Found at least one valid response in the REDIS storage
DEBUG http.handlers.cache Revalidate the request with the upstream server
DEBUG http.handlers.reverse_proxy selected upstream {"dial": "storefront-xxx-dev:3000", "total_upstreams": 1}
DEBUG http.handlers.reverse_proxy upstream roundtrip {"upstream": "storefront-xxx-dev:3000", "duration": 0.000108304, "request": {"remote_ip": "172.20.0.5", "remote_port": "48446", "client_ip": "172.20.0.5", "proto": "HTTP/1.1", "method": "GET", "host": "shop.apps.xxx.io", "uri": "/", "headers": {"Cache-Control": ["max-stale=300"], "Date": ["Fri, 23 Jan 2026 13:26:06 UTC"], "X-Forwarded-Server": ["4aedd6306c31"], "X-Forwarded-Host": ["shop.apps.xxx.io"], "X-Forwarded-For": ["172.20.0.5"], "Via": ["1.1 Caddy"], "User-Agent": ["Mozilla/5.0"], "X-Forwarded-Port": ["80"], "X-Forwarded-Proto": ["http"], "X-Real-Ip": ["172.20.0.5"], "Accept-Encoding": ["gzip"], "Accept": ["*/*"]}}, "error": "context canceled"}
Note the duration: 4 microseconds — the request fails instantly, not from a timeout.
Root Cause Analysis
We traced this extensively:
The SWR goroutine in middleware.go clones the request with context.Background() correctly
However, Caddy's reverse_proxy handler internally captures the original request's context during handler chain setup
When Souin sends the main response, Caddy cancels the original context
The background revalidation call through next(bgWriter, bgReq) reaches reverse_proxy, which still uses the (now canceled) original context
The upstream roundtrip fails immediately
When using Souin as a Caddy plugin, the stale-while-revalidate (SWR) feature partially works: stale content is served correctly, but the background revalidation always fails with "context canceled". This happens because Caddy cancels the request context immediately after the main response is sent, before the background goroutine can complete the upstream request.
Environment
5dfcbddeand2e1f6692)Caddyfile Configuration
Steps to Reproduce
Cache-Control: max-stale=300Expected Behavior
Actual Behavior
Debug Logs
Note the duration: 4 microseconds — the request fails instantly, not from a timeout.
Root Cause Analysis
We traced this extensively:
middleware.goclones the request withcontext.Background()correctlyreverse_proxyhandler internally captures the original request's context during handler chain setupnext(bgWriter, bgReq)reachesreverse_proxy, which still uses the (now canceled) original context