Styling custom error page

1. Output of caddy version:

2.6.2

2. How I run Caddy:

docker-compose

a. System environment:

Docker

c. Service/unit/compose file:

  caddy:
    image: caddy:2.6-alpine
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443/tcp
      - 443:443/udp
    volumes:
      - ./Docker/compose/local/caddy/Caddyfile:/etc/caddy/Caddyfile
      - ./Docker/compose/common/caddy/static:/etc/caddy/static
      - caddy_data:/data
      - caddy_config:/config
      - ./certs:/certs
      - django_static:/etc/caddy/django_static

d. My complete Caddy config:

{
  local_certs
  skip_install_trust
}

local.vfxtricks.com {
	tls /certs/localhost.crt /certs/localhost.key

	handle /_nuxt/hmr/ {
		reverse_proxy http://nuxt:24678
	}

	handle /api {
		reverse_proxy http://django:8000
	}

	handle /admin* {
		reverse_proxy http://django:8000
	}

	handle /static/* {
		reverse_proxy http://django:8000
	}

	handle {
		reverse_proxy http://nuxt:3000 {
			transport http {
				dial_timeout 0.1s
			}
		}
	}


	# handle errors
	handle_errors {
		rewrite * /error.html
		file_server {
			root /etc/caddy/static/
		}
	}
}

3. The problem I’m having:

I cannot load style.css file from my error.html file.

This is my folder structure:

/etc/caddy/static/
├─ error.html
├─ style.css

This is how I reference it in error.html:

<link rel="stylesheet" href="style.css" />

I can successfully display error.html page, but css is missing.

4. Error messages and/or full log output:

vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.3110847,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"nuxt:3000","duration":0.100261545,"request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/style.css","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["no-cors"],"Accept-Language":["en-US,en;q=0.9"],"Cookie":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Encoding":["gzip, deflate, br"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Sec-Fetch-Dest":["style"],"Sec-Fetch-Site":["same-origin"],"X-Forwarded-For":["172.18.0.1"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["text/css,*/*;q=0.1"],"Referer":["https://local.vfxtricks.com/"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["local.vfxtricks.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"error":"dial tcp: lookup nuxt: i/o timeout"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.311191,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/style.css","headers":{"Referer":["https://local.vfxtricks.com/"],"Cookie":[],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["text/css,*/*;q=0.1"],"Sec-Fetch-Site":["same-origin"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br"],"Sec-Fetch-Dest":["style"],"Accept-Language":["en-US,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Sec-Fetch-Mode":["no-cors"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"method":"GET","uri":"/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.311201,"logger":"http.handlers.file_server","msg":"sanitized path join","site_root":"/etc/caddy/static/","request_path":"/error.html","result":"/etc/caddy/static/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.3112323,"logger":"http.handlers.file_server","msg":"opening file","filename":"/etc/caddy/static/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.311306,"logger":"http.log.error","msg":"dial tcp: lookup nuxt: i/o timeout","request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/style.css","headers":{"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br"],"Accept-Language":["en-US,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Sec-Fetch-Mode":["no-cors"],"Sec-Fetch-Dest":["style"],"Cookie":[],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["text/css,*/*;q=0.1"],"Sec-Fetch-Site":["same-origin"],"Referer":["https://local.vfxtricks.com/"],"Sec-Ch-Ua-Platform":["\"Windows\""]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"duration":0.100461738,"status":502,"err_id":"pgc1crcp2","err_trace":"reverseproxy.statusError (reverseproxy.go:1272)"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.462671,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"nuxt:3000","total_upstreams":1}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.5634959,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"nuxt:3000","duration":0.100728728,"request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/favicon.ico","headers":{"Accept-Language":["en-US,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Pragma":["no-cache"],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["image"],"X-Forwarded-Host":["local.vfxtricks.com"],"Sec-Fetch-Site":["same-origin"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"Sec-Fetch-Mode":["no-cors"],"Accept-Encoding":["gzip, deflate, br"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Referer":["https://local.vfxtricks.com/"],"Cookie":[]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"error":"dial tcp: lookup nuxt: i/o timeout"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.563615,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/favicon.ico","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Fetch-Mode":["no-cors"],"Accept-Encoding":["gzip, deflate, br"],"Referer":["https://local.vfxtricks.com/"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-US,en;q=0.9"],"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["image"],"Cookie":[]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"method":"GET","uri":"/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.5636299,"logger":"http.handlers.file_server","msg":"sanitized path join","site_root":"/etc/caddy/static/","request_path":"/error.html","result":"/etc/caddy/static/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.5636683,"logger":"http.handlers.file_server","msg":"opening file","filename":"/etc/caddy/static/error.html"}
vfxtricks_local_caddy  | {"level":"debug","ts":1667327462.5637743,"logger":"http.log.error","msg":"dial tcp: lookup nuxt: i/o timeout","request":{"remote_ip":"172.18.0.1","remote_port":"49060","proto":"HTTP/2.0","method":"GET","host":"local.vfxtricks.com","uri":"/favicon.ico","headers":{"Referer":["https://local.vfxtricks.com/"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-US,en;q=0.9"],"Cookie":[],"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["image"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\""],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Fetch-Mode":["no-cors"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"local.vfxtricks.com"}},"duration":0.1009379,"status":502,"err_id":"4wzituk9j","err_trace":"reverseproxy.statusError (reverseproxy.go:1272)"}

5. What I already tried:

6. Links to relevant resources:

You need to use the root directive to tell Caddy where it should find your static files.

I recommend mounting them in /srv inside your Docker container.

Edit: Sorry, derp, you are using the root subdirective for file_server. I’d recommend using the root directive anyways because it’ll apply to all subdirectives and not just file_server (e.g. the file matcher, etc).

So the problem now that I look more closely :sweat_smile: is that your browser will make a second request to load the CSS file, but it’ll end up getting routed to (I think) your Django app instead by that handle.

You’d need to set up another handle which catches requests to your static error pages. You might need to use a subpath like /error* for this, and adjust your HTML to use /error/style.css to reach it.

1 Like

got it!
I reorganized folders/files and made extra handler for /errors/ to handle files.

thanks for help.

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.