Some questions regarding the cache-handler (souin) module and WordPress

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:

Screenshot From 2024-11-27 18-35-48

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)

Hello @feverdreamglider I’m asking myself, should the bypass_response should ignore Cache-Control HTTP response header values? If I implement this behavior this will (should?) solve this issue.

1 Like

Ah, I didn’t consider bypass_response at all. I suppose the cache-control header is the reason why the ttl is only 1h instead of 1w.

My focus was more on request headers. When a client sends a request header to bypass cache (whether with pragma: no-cache, cache-control: no-cache, or similar), I would prefer souin to keep serving from cache. I thought bypass_request might do the trick but I don’t think it did? Does anything in my Caddyfile look off to you?

I also face this issue but after read your thread i understand my issue.