1. The problem I’m having:
I have a service that can serve for various domains (fronted by Caddy). For one particular domain (the “main” one) I want to serve a static blog. The problem is I can’t seem to get Caddy to serve it.
2. Error messages and/or full log output:
There is no error. The request returns a 404 and the application log (the backend that Caddy is reverse proxy for) does not receive the request. So Caddy appears to be attempting to serve it but the response is a 404 (see log entry below)
2024/09/30 23:19:59.765 INFO http.log.access.log0 handled request {"request": {"remote_ip": "186.77.197.105", "remote_port": "20503", "client_ip": "186.77.197.105", "proto": "HTTP/2.0", "method": "GET", "host": "linktaco.com", "uri": "/blog/index.html", "headers": {"Accept-Encoding": ["gzip, deflate, br, zstd"], "Upgrade-Insecure-Requests": ["1"], "Sec-Fetch-Mode": ["navigate"], "Sec-Fetch-Site": ["cross-site"], "Te": ["trailers"], "User-Agent": ["Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"], "Accept-Language": ["en-US,en;q=0.5"], "Dnt": ["1"], "Cookie": ["REDACTED"], "Sec-Fetch-Dest": ["document"], "Priority": ["u=0, i"], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "linktaco.com"}}, "bytes_read": 0, "user_id": "", "duration": 0.000150039, "size": 0, "status": 404, "resp_headers": {"Server": ["Caddy"], "X-Frame-Options": ["DENY"], "Permissions-Policy": ["interest-cohort=()"], "Referrer-Policy": ["no-referrer-when-downgrade"], "Alt-Svc": ["h3=\":443\"; ma=2592000"], "X-Xss-Protection": ["1; mode=block"], "Content-Security-Policy": ["default-src * 'unsafe-inline'; img-src * 'unsafe-inline'; style-src * 'unsafe-inline'"], "Strict-Transport-Security": ["max-age=31536000;"], "X-Content-Type-Options": ["nosniff"]}}
The directory permissions are permissive (0755) all the way so there should be no issue reading. I verified here:
# su -m www -c 'ls /usr/home/links/blog'
index.html
The only caddy specific changes in the /etc/rc.conf
file are:
caddy_enable="YES"
caddy_user="www"
caddy_group="www"
3. Caddy version:
v2.8.4
4. How I installed and ran Caddy:
pkg install caddy
a. System environment:
FreeBSD ah-web01 14.1-RELEASE-p4 FreeBSD 14.1-RELEASE-p4 GENERIC amd64
b. Command:
service caddy start
c. Service/unit/compose file:
Default FreeBSD service file
#!/bin/sh
. /etc/rc.subr
name=caddy
rcvar=caddy_enable
desc="Powerful, enterprise-ready, open source web server with automatic HTTPS written in Go"
load_rc_config $name
# Defaults
: ${caddy_enable:=NO}
: ${caddy_adapter:=caddyfile}
: ${caddy_config:="/usr/local/etc/caddy/Caddyfile"}
: ${caddy_admin:="unix//var/run/${name}/${name}.sock"}
: ${caddy_command:="/usr/local/bin/${name}"}
: ${caddy_directory:=/var/db/caddy}
: ${caddy_extra_flags:=""}
: ${caddy_logdir:="/var/log/${name}"}
: ${caddy_logfile:="${caddy_logdir}/${name}.log"}
: ${caddy_user:="root"}
: ${caddy_group:="wheel"}
# Config and base directories
: ${XDG_CONFIG_HOME:="${caddy_directory}/config"}
: ${XDG_DATA_HOME:="${caddy_directory}/data"}
export XDG_CONFIG_HOME XDG_DATA_HOME
# Default admin interface
export CADDY_ADMIN="${caddy_admin}"
command="${caddy_command}"
pidfile="/var/run/${name}/${name}.pid"
required_files="${caddy_config} ${caddy_command}"
start_precmd="caddy_precmd"
start_cmd="caddy_start"
stop_precmd="caddy_prestop"
# JSON is the native format, so there is no "adapter" for it
if [ "${caddy_adapter}" = "json" ]; then
caddy_flags="--config ${caddy_config}"
else
caddy_flags="--config ${caddy_config} --adapter ${caddy_adapter}"
fi
# Extra Commands
extra_commands="configtest reload reloadssl"
configtest_cmd="caddy_execute validate ${caddy_flags}"
reload_cmd="caddy_execute reload ${caddy_flags}"
reloadssl_cmd="caddy_execute reload --force ${caddy_flags}"
caddy_execute()
{
/usr/bin/su -m "${caddy_user}" -c "${caddy_command} $*"
}
caddy_precmd()
{
# Create required directories and set permissions
/usr/bin/install -d -m 755 -o "${caddy_user}" -g "${caddy_group}" ${caddy_directory}
/usr/bin/install -d -m 700 -o "${caddy_user}" -g "${caddy_group}" ${caddy_directory}/config
/usr/bin/install -d -m 700 -o "${caddy_user}" -g "${caddy_group}" ${caddy_directory}/data
/usr/bin/install -d -m 755 -o "${caddy_user}" -g "${caddy_group}" ${caddy_logdir}
/usr/bin/install -d -m 700 -o "${caddy_user}" -g "${caddy_group}" /var/run/caddy
if [ -e ${caddy_logfile} ]; then
/bin/chmod 644 ${caddy_logfile}
/usr/sbin/chown "${caddy_user}:${caddy_group}" ${caddy_logfile}
else
/usr/bin/install -m 644 -o "${caddy_user}" -g "${caddy_group}" /dev/null ${caddy_logfile}
fi
}
caddy_start()
{
echo -n "Starting caddy... "
/usr/bin/su -m ${caddy_user} -c "${caddy_command} start ${caddy_flags} \
${caddy_extra_flags} --pidfile ${pidfile}" >> ${caddy_logfile} 2>&1
if [ $? -eq 0 ] && ps -ax -o pid | grep -q "$(cat ${pidfile})"; then
echo "done"
echo "Log: ${caddy_logfile}"
else
echo "Error: Caddy failed to start"
echo "Check the caddy log: ${caddy_logfile}"
fi
}
caddy_prestop()
{
local result
echo -n "Stopping caddy... "
result="$(caddy_execute stop ${caddy_flags} 2>&1)"
if [ ${?} -eq 0 ]; then
echo "done"
exit 0
else
if echo "${result}" | grep -q -e "connection refused" \
-e "connect: no such file or directory"; then
echo "admin interface unavailable; using pidfile"
return 0
else
echo "Error: Unable to stop caddy"
echo "Check the caddy log: ${caddy_logfile}"
return 1
fi
fi
}
run_rc_command "$1"
d. My complete Caddy config:
{
on_demand_tls {
ask http://localhost:5004/_check/domain
}
debug
}
https:// {
bind 174.136.99.171
tls {
on_demand
}
header {
# disable FLoC tracking
Permissions-Policy interest-cohort=()
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# clickjacking protection
X-Frame-Options DENY
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
Content-Security-Policy "default-src * 'unsafe-inline'; img-src * 'unsafe-inline'; style-src * 'unsafe-inline'"
X-XSS-Protection "1; mode=block"
}
@ltblog {
host linktaco.com
path /blog/*
}
handle @ltblog {
root * /usr/home/links/blog
file_server
}
log {
output file /var/log/caddy/links-access.log
format console
}
reverse_proxy localhost:5000
encode zstd gzip
}