1. The problem I’m having:
I’m trying to send a preflight request to obtain a User-Context-Hash header, which will then allow me to cache private data.
The cache always misses. I haven’t explored all the configuration options yet, but without the user context, the cache was working. I’m not sure if the cache can work within an intercept directive.
Could someone please provide me with some guidance to succeed, or confirm if it’s not possible, if that’s the case?
2. Error messages and/or full log output:
2025-01-15T06:24:02.751635477Z 2025/01/15 06:24:02.751 DEBUG http.stdlib http: TLS handshake error from 172.19.0.1:59044: remote error: tls: unknown certificate
2025-01-15T06:24:02.752428600Z 2025/01/15 06:24:02.752 DEBUG events event {"name": "tls_get_certificate", "id": "fca022d7-f940-4ab5-813e-1f6d0c39e042", "origin": "tls", "data": {"client_hello":{"CipherSuites":[31354,4865,4866,4867,49195,49199,49196,49200,52393,52392,49171,49172,156,157,47,53],"ServerName":"localhost","SupportedCurves":[10794,4588,29,23,24],"SupportedPoints":"AA==","SignatureSchemes":[1027,2052,1025,1283,2053,1281,2054,1537],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[6682,772,771],"RemoteAddr":{"IP":"172.19.0.1","Port":59052,"Zone":""},"LocalAddr":{"IP":"172.19.0.9","Port":443,"Zone":""}}}}
2025-01-15T06:24:02.752448952Z 2025/01/15 06:24:02.752 DEBUG tls.handshake choosing certificate {"identifier": "localhost", "num_choices": 1}
2025-01-15T06:24:02.752451449Z 2025/01/15 06:24:02.752 DEBUG tls.handshake custom certificate selection results {"identifier": "localhost", "subjects": ["localhost"], "managed": false, "issuer_key": "", "hash": "eaef97f8d3bb92ba2b1671f45688a7271bfa97856eef560762546bf10372bd52"}
2025-01-15T06:24:02.752453916Z 2025/01/15 06:24:02.752 DEBUG tls.handshake matched certificate in cache {"remote_ip": "172.19.0.1", "remote_port": "59052", "subjects": ["localhost"], "managed": false, "expiration": "2026/01/14 12:54:15.000", "hash": "eaef97f8d3bb92ba2b1671f45688a7271bfa97856eef560762546bf10372bd52"}
2025-01-15T06:24:02.754437656Z 2025/01/15 06:24:02.754 DEBUG http.handlers.rewrite rewrote request {"request": {"remote_ip": "172.19.0.1", "remote_port": "59052", "client_ip": "172.19.0.1", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "index.php?page=1&itemsPerPage=2", "headers": {"Referer": ["https://localhost/api/docs"], "Priority": ["u=1, i"], "Cache-Control": ["no-cache"], "Accept": ["application/ld+json"], "Sec-Ch-Ua": ["\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""], "Sec-Fetch-Site": ["same-origin"], "Sec-Fetch-Dest": ["empty"], "Sec-Ch-Ua-Platform": ["\"Linux\""], "Accept-Encoding": ["gzip, deflate, br, zstd"], "Accept-Language": ["fr,en;q=0.9"], "Pragma": ["no-cache"], "Authorization": ["REDACTED"], "User-Agent": ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"], "Sec-Fetch-Mode": ["cors"], "Dnt": ["1"], "Sec-Ch-Ua-Mobile": ["?0"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "localhost"}}, "method": "GET", "uri": "index.php?page=1&itemsPerPage=2"}
2025-01-15T06:24:02.858786281Z 2025/01/15 06:24:02.858 ERROR the current responseWriter is not a flusher {"error": "feature not supported"}
2025-01-15T06:24:02.892363889Z 2025/01/15 06:24:02.892 DEBUG http.handlers.intercept handling response {"handler": 0}
2025-01-15T06:24:02.892422451Z 2025/01/15 06:24:02.892 DEBUG http.handlers.cache Incomming request &{Method:GET URL:index.php?page=1&itemsPerPage=2 Proto:HTTP/2.0 ProtoMajor:2 ProtoMinor:0 Header:map[Accept:[application/ld+json] Accept-Encoding:[gzip, deflate, br, zstd] Accept-Language:[fr,en;q=0.9] Authorization:[user-pharma-admin] Cache-Control:[no-cache] Dnt:[1] Pragma:[no-cache] Priority:[u=1, i] Referer:[https://localhost/api/docs] Sec-Ch-Ua:["Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"] Sec-Ch-Ua-Mobile:[?0] Sec-Ch-Ua-Platform:["Linux"] Sec-Fetch-Dest:[empty] Sec-Fetch-Mode:[cors] Sec-Fetch-Site:[same-origin] User-Agent:[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36]] Body:0xc000ea2a68 GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:localhost Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:172.19.0.1:59052 RequestURI:index.php?page=1&itemsPerPage=2 TLS:0xc000e400c0 Cancel:<nil> Response:<nil> Pattern: ctx:0xc0020cf440 pat:<nil> matches:[] otherValues:map[]}
2025-01-15T06:24:02.892540556Z 2025/01/15 06:24:02.892 DEBUG http.handlers.cache Request cache-control &{MaxAge:-1 MaxStale:-1 MaxStaleSet:false MinFresh:-1 NoCache:true NoStore:false NoTransform:false OnlyIfCached:false StaleIfError:0 Extensions:[]}
2025-01-15T06:24:02.892549997Z 2025/01/15 06:24:02.892 DEBUG http.handlers.cache Request the upstream server
2025-01-15T06:24:03.106640298Z 2025/01/15 06:24:03.106 ERROR the current responseWriter is not a flusher {"error": "feature not supported"}
2025-01-15T06:24:03.133720302Z 2025/01/15 06:24:03.133 DEBUG http.handlers.cache Response cache-control &{MustRevalidate:false NoCache:map[] NoCachePresent:true NoStore:false NoTransform:false Public:true Private:map[] PrivatePresent:true ProxyRevalidate:false MaxAge:2147483647 SMaxAge:-1 Immutable:false StaleIfError:-1 StaleWhileRevalidate:-1 Extensions:[]}
2025-01-15T06:24:03.133733226Z 2025/01/15 06:24:03.133 DEBUG http.handlers.cache Store the response for GET-https-localhost-index.php?page=1&itemsPerPage=2{-VARY-}Origin:fixed;Accept:application/ld+json;User-Context-Hash:TODO with duration 596523h14m5.866413431s
2025-01-15T06:24:03.138696790Z 2025/01/15 06:24:03.138 DEBUG http.handlers.cache Store the new mapping for the key GET-https-localhost-index.php?page=1&itemsPerPage=2{-VARY-}Origin:fixed;Accept:application/ld+json;User-Context-Hash:TODO in Default
2025-01-15T06:24:03.138719937Z 2025/01/15 06:24:03.138 DEBUG http.handlers.cache Stored the key GET-https-localhost-index.php?page=1&itemsPerPage=2{-VARY-}Origin:fixed;Accept:application/ld+json;User-Context-Hash:TODO in the DEFAULT provider
2025-01-15T06:24:03.139476748Z 2025/01/15 06:24:03.139 DEBUG http.handlers.cache Store the tag /api/.well-known/genid/50407300e13f9a2f0dd7
2025-01-15T06:24:03.139501366Z 2025/01/15 06:24:03.139 DEBUG http.handlers.cache Store the tag /api/.well-known/genid/5926db2e8fa6f9ca0519
2025-01-15T06:24:03.139774749Z 2025/01/15 06:24:03.139 INFO http.log.access.log0 handled request {"request": {"remote_ip": "172.19.0.1", "remote_port": "59052", "client_ip": "172.19.0.1", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "/api/trades?itemsPerPage=2&page=1", "headers": {"Cache-Control": ["no-cache"], "Sec-Ch-Ua": ["\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""], "Sec-Fetch-Site": ["same-origin"], "Sec-Ch-Ua-Platform": ["\"Linux\""], "Accept-Language": ["fr,en;q=0.9"], "Dnt": ["1"], "Referer": ["https://localhost/api/docs"], "Accept-Encoding": ["gzip, deflate, br, zstd"], "Pragma": ["no-cache"], "Accept": ["application/ld+json"], "Priority": ["u=1, i"], "Authorization": ["REDACTED"], "Sec-Fetch-Mode": ["cors"], "Sec-Fetch-Dest": ["empty"], "Sec-Ch-Ua-Mobile": ["?0"], "User-Agent": ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "localhost"}}, "bytes_read": 0, "user_id": "", "duration": 0.385333389, "size": 1753, "status": 200, "resp_headers": {"Content-Encoding": ["zstd"], "Alt-Svc": ["h3=\":443\"; ma=2592000"], "Date": ["Wed, 15 Jan 2025 06:24:02 GMT", "Wed, 15 Jan 2025 06:24:03 GMT"], "X-Content-Type-Options": ["nosniff"], "X-Frame-Options": ["deny"], "Etag": ["\"faa2335d00513cb854bc8c5b6d04e1d2-zstd\""], "Server": ["Caddy"], "Content-Type": ["application/json", "application/ld+json; charset=utf-8"], "X-Debug-Token": ["7f36a7", "b11e8b"], "X-Debug-Token-Link": ["https://localhost/_profiler/7f36a7", "https://localhost/_profiler/b11e8b"], "X-Robots-Tag": ["noindex", "noindex"], "Link": ["<https://localhost/api/docs.jsonld>; rel=\"http://www.w3.org/ns/hydra/core#apiDocumentation\""], "Surrogate-Key": ["/api/.well-known/genid/50407300e13f9a2f0dd7, /api/.well-known/genid/5926db2e8fa6f9ca0519, /api/trades"], "Permissions-Policy": ["browsing-topics=()"], "Vary": ["Origin", "Accept", "User-Context-Hash", "Accept-Encoding"], "Cache-Status": ["Souin; fwd=uri-miss; stored; key=GET-https-localhost-index.php?page=1&itemsPerPage=2"]}}
3. Caddy version:
4. How I installed and ran Caddy:
a. System environment:
From docker image dunglas/frankenphp:1.3-php8.4-alpine.
d. My complete Caddy config:
{
{$CADDY_GLOBAL_OPTIONS}
debug
frankenphp {
{$FRANKENPHP_CONFIG}
}
cache {
log_level debug
mode bypass
redis {
configuration {
url redis:6379
}
}
}
}
{$CADDY_EXTRA_CONFIG}
{$SERVER_NAME:localhost} {
tls /etc/caddy/server.crt /etc/caddy/server.key {
key_type rsa2048
}
log {
{$CADDY_SERVER_LOG_OPTIONS}
# Redact the authorization query parameter that can be set by Mercure
format filter {
request>uri query {
replace authorization REDACTED
}
}
level DEBUG
}
root /app/public
encode zstd br gzip
{$CADDY_SERVER_EXTRA_DIRECTIVES}
# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
header ?Permissions-Policy "browsing-topics=()"
intercept {
@readyToCache {
status 200
header User-Context-Hash *
}
handle_response @readyToCache {
request_header Origin fixed
request_header User-Context-Hash {resp.header.User-Context-Hash}
header -User-Context-Hash
header -Cache-Control
cache
rewrite @phpRoute index.php
php @frontController
}
handle_response {
}
}
@phpRoute {
not path /.well-known/mercure*
not file {path}
}
rewrite @phpRoute index.php
@file {
file {path}
}
file_server @file
@frontController path index.php
php @frontController
}