Hi, first of all thank you for the amazing work on the Caddy server, I have just started using it recently and I’m loving it.
1. The problem I’m having:
I’m trying to add Early Hints to the new site I’m building, I’m using vite and react-router v7 and I have a script that checks the manifest generated by vite for each route and adds the Link header with the assets I’d like to fetch with the 103 Early Hints. I have tried adding the respond 103 to my Caddyfile pretty much everywhere, and I also tried moving the script from my entry.server.jsx directly to a early-hints.caddy file that I was then importing inside my Caddyfile but nothing seemed to work.
The documentation doesn’t provide enough information on how to set this up so I don’t even know what I’m doing wrong here, some guidance would be very appreciated.
2. Error messages and/or full log output:
There’s no error messages, I just can’t seem to get the 103 requests to work.
3. Caddy version:
caddy --version
v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=
4. How I installed and ran Caddy:
I have installed Caddy via xcaddy (v0.4.5_linux_amd64) with the following command:
xcaddy build --with github.com/darkweak/souin/plugins/caddy --with github.com/ueffel/caddy-brotli --with github.com/darkweak/storages/redis/caddy
a. System environment:
Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-87-generic x86_64)
Running two Docker containers inside, one for WordPress and the other for the React app.
b. Command:
caddy start --config /etc/caddy/Caddyfile
c. Service/unit/compose file:
d. My complete Caddy config:
# Global options
{
# Email for Let's Encrypt
email dev.team@carney.co
# Souin Cache Configuration
order cache before rewrite
cache {
# Cache storage backend - Redis for production
redis {
url localhost:6379
}
ttl 1h
stale 1h
# Cache key configuration
key {
disable_body
disable_host
disable_method
}
log_level INFO
allowed_http_verbs GET HEAD
# Cache management API
api {
basepath /api/cache
souin
}
}
}
# WordPress Admin Server (wpadmin.carney.co)
wpadmin.carney.co {
# Increase upload size limit
request_body {
max_size 5120MB
}
# Redirect root to /wp-admin
@root path /
redir @root /wp-admin 301
# Proxy all requests to WordPress
reverse_proxy localhost:8080
}
# Main Site (beta.carney.co)
beta.carney.co {
# Enable compression
encode br gzip
respond 103
# Static assets - serve from local filesystem
@static_assets {
path_regexp static ^/(assets|lottie)/.*\.(js|css|json)$
}
handle @static_assets {
root * /var/www/static-assets
# Add caching headers
header Cache-Control "public, max-age=31536000"
header Vary "Accept-Encoding"
respond 103
file_server {
precompressed br gzip
}
}
# Daily carnage routes - new posts daily
handle /daily-carnage* {
# Cache for 24 hours (refreshes daily with new content)
cache {
ttl 24h
stale 48h # Serve stale for 48h if backend is down
}
respond 103
reverse_proxy localhost:3060
}
# Cache eviction endpoint for Apollo
handle /api/cache/evict {
reverse_proxy localhost:3060
}
handle {
# Aggressive caching since content rarely changes
cache {
ttl 7d # Cache for 7 days (pages almost never change)
stale 14d # Serve stale for up to 14 days if backend is down
# Respect cache headers from upstream
# If Remix sets Cache-Control: no-cache, it won't be cached
# Use /api/cache/evict during deployments to clear cache
}
respond 103
reverse_proxy localhost:3060
}
}