Django and Allowed Hosts Blocking Health Check

1. Caddy version (caddy version):

caddy:2.3.0 in Docker

2. How I run Caddy:

a. System environment:

Running in Docker Container. Docker version 20.10.5, build 55c4c88 on Ubuntu 18.04.5 LTS

b. Command:

docker run -d -it --restart="always" \
    -v /home/ocom/apps/data/caddy2/Caddyfile:/etc/caddy/Caddyfile \
    -v /home/ocom/apps/data/caddy2/data:/data \
    -v /home/ocom/apps/data/caddy2/conf:/config \
    -v /home/ocom/apps/data/caddy2/log:/log \
    --network bridge \
    --name caddy2 -p 80:80 -p 443:443  -e CADDY_OPTIONS="--email letsencrypt@xxx --agree" -d  caddy2:ocom

c. Service/unit/compose file:

FROM caddy:2.3.0-builder AS builder

RUN xcaddy build --with

FROM caddy:2.3.0-alpine

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

d. My complete Caddyfile or JSON config: {
        log {
            output file /log/ {
                rotate_size 100 # Rotate a log when it reaches 100 MB
                rotate_age  14  # Keep rotated log files for 14 days
                rotate_keep 10  # Keep at most 10 rotated log files

#        read_timeout 20h
        tls {
#               dns lego_deprecated linode

        reverse_proxy /* {
            # websocket
                transport http {
                    keepalive off

            header_up X-Forwarded-For {host}
            header_up Host {http.reverse_proxy.upstream.hostport}
            header_up X-Real-IP {http.reverse-proxy.upstream.address}
            header_up X-Forwarded-Port {http.request.port}
            header_up X-Forwarded-Host {}

            health_path /dockerStatus/
            health_status 2XX
            health_interval  7s
            health_timeout  60s

            lb_policy least_conn
            # fail_timeout 2s
            max_fails 10
            lb_try_interval 5s
        encode gzip zstd


3. The problem I’m having:

I have used Caddy 1 successfully for a few years so I am trying to upgrade to V2

I am finding with my health check that it’s not working with Django because by default it wants the hostname to match. But it seems the header_up is Not used in the health check is that correct? Has that changed? I have looked in the documentation but I could not find anything.

My Django app has this in the settings

VHOST = os.environ.get('VIRTUAL_HOST')

Where the VIRTUAL_HOST env var is passed into the Docker container.

4. Error messages and/or full log output:

On my Django container I get this
[pid: 27|app: 0|req: 1/1] () {32 vars in 386 bytes} [Fri Mar 19 05:45:45 2021] GET /dockerStatus/ => generated 26 bytes in 1108 msecs (HTTP/1.1 400) 2 headers in 67 bytes (1 switches on core 0)
[pid: 26|app: 0|req: 1/2] () {32 vars in 386 bytes} [Fri Mar 19 05:45:52 2021] GET /dockerStatus/ => generated 26 bytes in 1117 msecs (HTTP/1.1 400) 2 headers in 67 bytes (1 switches on core 0)
[pid: 26|app: 0|req: 2/3] () {32 vars in 386 bytes} [Fri Mar 19 05:45:59 2021] GET /dockerStatus/ => generated 26 bytes in 46 msecs (HTTP/1.1 400) 2 headers in 67 bytes (1 switches on core 3)
[pid: 26|app: 0|req: 3/4] () {32 vars in 386 bytes} [Fri Mar 19 05:46:06 2021] GET /dockerStatus/ => generated 26 bytes in 38 msecs (HTTP/1.1 400) 2 headers in 67 bytes (1 switches on core 1)

Because it’s doing the check but rejecting it.

I would appreciate any help or suggestions.

5. What I already tried:

I have tried different header_up incantations but nothing seems to work.

6. Links to relevant resources:

You can get rid of the /* here. Using a matcher like this is less efficient than either using *, or omitting the matcher altogether, because /* tells Caddy to match on paths that start with /, which will always be true; i.e. it makes Caddy do an extra string comparison on every request. Very minor performance difference (nanoseconds), but it also reads cleaner to remove 3 characters from your config :grin:

Please remove these lines; Caddy probably sets these headers correctly automatically:

You’re telling Caddy to change the Host to, and X-Real-IP to are you sure that’s what you want? It’s typically more useful to have Host be the actual domain. The X-Forwarded-For header is meant to be the client’s IP address, not the domain.

I don’t think you need this for websockets. It should work correctly out of the box. Was there a specific problem you experienced that caused you to add it?

That’s correct. v2.4.0 will have an option health_headers to set them. You can build against v2.4.0-beta.1 if you need that now:

@francislavoie Thank you for your response. I will try 2.4-beta

JUST FYI for anyone else looking at this, this is my first attempt at migrating from V1 to V2 so some parts come from me trying to change V1 to V2 and may not be needed.

I can confirm this works. Seems the placeholders don’t work as expected but if I hard code the headers it works.

Active health checks don’t happen in a request context, so most placeholders won’t work. They happen in their own coroutines on a timer.

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