1. The problem I’m having:
tl;dr I want some custom settings for the cache-handler Caddy module: have a cache expiry time of 1 week, save the cache somewhere on disk instead of being only in memory, and don’t bypass cache for clients who request it for any reason
Long version:
I am switching from WordPress cache plugins to the cache-handler module for Caddy. I have gotten the basics to work thanks to this doc, but am struggling to customize some options for my use-case.
I want a simple setup similar to fastcgi cache on nginx, as described in this article. A storage option that does both in-memory and disk caching (later as fallback) would be fantastic. If not, one that only supports disk based caching is also fine.
I am using badger but am happy to switch to a different storage option that will suit me better.
I set a ttl of 1 week as the content doesn’t change very often, but looks like that’s being overridden by the cache-control response header (cache-control: public, max-age=3600
).
Lastly, I would like the caching to work even when client requests no-cache (like when I have disabled caching in browser dev tools). I tried mode bypass_request
but it didn’t work and clearly I have no idea what I’m doing.
2. Error messages and/or full log output:
There aren’t any error messages with badger. I had some error messages with otter, but I haven’t seen them when using badger.
Nov 27 15:23:17 speedycp.ashutoshmishra.org caddy[51905]: {"level":"error","ts":1732701197.9567173,"logger":"storages.cache.otter","msg":"Impos
sible to get the mapping key IDX_GET-https-techorbiter.com-/automatically-update-wordpress-org-plugins-themes/356/ in Otter"}
Nov 27 15:23:54 speedycp.ashutoshmishra.org caddy[51905]: {"level":"error","ts":1732701234.8226042,"logger":"storages.cache.otter","msg":"Impos
sible to get the mapping key IDX_GET-https-techorbiter.com-/wp-content/uploads/VirtualBoxVM_lLsxBVVUG5.png in Otter"}
Nov 27 15:24:21 speedycp.ashutoshmishra.org caddy[51905]: {"level":"error","ts":1732701261.090649,"logger":"storages.cache.otter","msg":"Imposs
ible to get the mapping key IDX_GET-https-techorbiter.com-/its-time-to-say-goodbye/614/comment-page-1/ in Otter"}
3. Caddy version:
2.8.4
4. How I installed and ran Caddy:
Compiled with xcaddy to include modules for cache-handler, otter, badger, cloudflare-dns, and porkbun-dns. Placed the caddy binary in /usr/bin
, added the systemd service unit to /etc/systemd/system
, and added Caddyfile
to /etc/caddy
.
xcaddy build \
--with github.com/caddyserver/cache-handler \
--with github.com/caddy-dns/cloudflare \
--with github.com/caddy-dns/porkbun \
--with github.com/darkweak/storages/badger/caddy \
--with github.com/darkweak/storages/otter/caddy
a. System environment:
Debian 12.latest 64-bit (VPS)
b. Command:
n/a
c. Service/unit/compose file:
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=user
Group=user
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
# Automatically restart caddy if it crashes except if the exit code was 1
RestartPreventExitStatus=1
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
d. My complete Caddy config:
Here’s the cache-handler part of the Caddyfile:
@souin-cache {
not header_regexp Cookie "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_items_in_cart|wp_woocommerce_session"
not path_regexp "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(index)?.xml|[a-z0-9-]+-sitemap([0-9]+)?.xml)"
not method POST
not expression {query} != ''
}
cache @souin-cache {
ttl 1w
stale 1d
badger
#otter
default_cache_control "public, max-age=3600"
timeout {
cache 100ms
}
#mode bypass_request
}
Here’s the full Caddyfile:
{
cache
}
techorbiter.com {
root * /home/mishra/web/{host}/public
encode zstd gzip
@disallowed {
path /xmlrpc.php /wp-config.php /.* *.sql *.gz *.zst *.br
}
respond @disallowed 403
@souin-cache {
not header_regexp Cookie "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_items_in_cart|wp_woocommerce_session"
not path_regexp "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(index)?.xml|[a-z0-9-]+-sitemap([0-9]+)?.xml)"
not method POST
not expression {query} != ''
}
cache @souin-cache {
ttl 1w
stale 1d
badger
#otter
default_cache_control "public, max-age=3600"
timeout {
cache 100ms
}
#mode bypass_request
}
php_fastcgi unix//run/php/php8.1-fpm.sock
file_server {
precompressed zstd br gzip
}
}
www.techorbiter.com {
redir https://techorbiter.com{uri}
}
5. Links to relevant resources:
Cache works but ttl is set to 3600 and cache is bypassed if client requests it (browser dev tools with cache disabled, pingdom tools, etc)