Custom error page for reverse proxy

1. The problem I’m having:

I can’t make a custom error page for reverse_proxy, so that when the frontend container shows the custom error page.
I have a caddy and frontend docker containers. The frontend folder has folder error_505, where html, css, svg files located. I make mount in docker-compose.yml in the caddy service on this folder like that:
volumes:

  • ./error_500:/usr/share/caddy/error_500/
    I tried handle_response and handle_error, but when i turn off frontend container and I go through URL, I get 502 status code, without a custom page, or white screen with 200 status code.

2. Error messages and/or full log output:

{"level":"error","ts":1695625064.578222,"logger":"http.log.error.log0","msg":"error handling handler error","request":{"remote_ip":"31.13.134.16","remote_port":"46928","client_ip":"31.13.134.16","proto":"HTTP/2.0","method":"GET","host":"unity.staging.donorsearch.org","uri":"/favicon.ico","headers":{"Accept":["image/avif,image/webp,*/*"],"Authorization":[],"Cookie":[],"Sec-Fetch-Dest":["image"],"Sec-Fetch-Site":["same-origin"],"User-Agent":["Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0"],"Accept-Language":["ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3"],"Cache-Control":["no-cache"],"Accept-Encoding":["gzip, deflate, br"],"Referer":["https://unity.staging.donorsearch.org/"],"Sec-Fetch-Mode":["no-cors"],"Pragma":["no-cache"],"Te":["trailers"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"unity.staging.donorsearch.org"}},"duration":0.00457702,"error":"{id=jm0v0igf8} fileserver.(*FileServer).notFound (staticfiles.go:613): HTTP 404","first_error":{"msg":"dial tcp: lookup unity_frontend on 127.0.0.11:53: server misbehaving","status":502,"err_id":"i5ytri1gq","err_trace":"reverseproxy.statusError (reverseproxy.go:1248)"}}
{"level":"error","ts":1695625064.578261,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"31.13.134.16","remote_port":"46928","client_ip":"31.13.134.16","proto":"HTTP/2.0","method":"GET","host":"unity.staging.donorsearch.org","uri":"/favicon.ico","headers":{"User-Agent":["Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0"],"Accept-Language":["ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3"],"Cache-Control":["no-cache"],"Accept-Encoding":["gzip, deflate, br"],"Referer":["https://unity.staging.donorsearch.org/"],"Sec-Fetch-Mode":["no-cors"],"Pragma":["no-cache"],"Te":["trailers"],"Accept":["image/avif,image/webp,*/*"],"Authorization":[],"Cookie":[],"Sec-Fetch-Dest":["image"],"Sec-Fetch-Site":["same-origin"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"unity.staging.donorsearch.org"}},"bytes_read":0,"user_id":"admin","duration":0.00457702,"size":0,"status":502,"resp_headers":{"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Strict-Transport-Security":["max-age=31536000;"]}}

3. Caddy version:

v2.7.4 h1:J8nisjdOxnYHXlorUKXY75Gr6iBfudfoGhrJ8t7/flI=

4. How I installed and ran Caddy:

a. System environment:

OS Linux Ubuntu 18.04, Docker version 19.03.12

b. Command:

docker-compose -f docker-compose.caddy.yml up -d --build

c. Service/unit/compose file:

version: "3.6"

networks:
  proxynet:

services:
  caddy:
    image: caddy:latest
    container_name: unity_caddy
    restart: unless-stopped
    env_file: ./config/.env
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile  # configuration
      - caddy-config:/config  # configuation autosaves
      - caddy-data:/data  # saving certificates
      - /etc/ssl:/etc/ssl
      - ./error_500:/usr/share/caddy/error_500/
    ports:
      - "80:80"
      - "443:443"
    networks:
      - proxynet

volumes:
  caddy-config:
  caddy-data:

d. My complete Caddy config:

{
  email {$TLS_EMAIL}
}
{$DOMAIN_NAME} {
  # HTTPS options:
  header Strict-Transport-Security max-age=31536000;

  # Removing some headers for improved security:
  header -Server
#  @error status 500 502 503
  # Serving dynamic requests:
  reverse_proxy unity_frontend:3000 {
        @error status 500 502 503
        handle_response @error {
                root    * /app
                rewrite * /app/error_500/index.html
                file_server
        }
  }
#  handle_errors {
#    rewrite * /usr/share/caddy/error_500/index.html
#    file_server
#  }

  basicauth /* {
    admin JDJhJDEwJE5LZ1NMLmVzVi9lSXBsSGhZTS5Mc2VxYnhIYmdmeWlxa09uUi5Pd3g3aXoxeTEvTGt3REJP
  }
  try_files {path} /index.html
   # Allows to use .gz files when available:
  encode gzip


  # Logs:
  log {
    output stdout
  }
}

5. Links to relevant resources:

handle_response only handles actual responses from upstream. That should work for 500 status, but not 502 and 503.

502 and 503 statuses are usually errors that need to be handled with handle_errors, because they’re actual errors emitted by Caddy’s proxy handler, and not responses from upstream.

I recommend you mount your files to /srv or /app or something like that, not /usr/share/caddy. That path is meant to just contain the default welcome page.

Make sure this matches where you put your files.

And make sure this is only includes the part of the path not including the root. So it should only be /error_500/index.html or something, otherwise Caddy will be looking for /app/app/error_500/index.html on disk (it concatenates the root and the request path together).

Good, i mounted error_500 to /srv, made correctly rewrite and it’s work! But i have problem. In the error_500 folder are css and svg files and they do not work, issuing 502 status code. How to configure this?

Since those files would not be loaded from the same request, you’ll need a route that handles the requests to your other assets.

Or you could inline those assets in your HTML file instead (CSS with <style> tags, SVG as-is because it’s just XML, binary images with base64 data uri, etc).

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