Default behavior of Caddy when listening on port 80

1. The problem I’m having:

Hi!

When the following configuration is set in Caddy Server:

:8080 {
  respond "Hello, world from 8080!"
}

:9090 {
  respond "Hello, world from 9090!"
}
{
   "apps":{
      "http":{
         "servers":{
            "srv0":{
               "listen":[
                  ":8080"
               ],
               "routes":[
                  {
                     "handle":[
                        {
                           "body":"Hello, world from 8080!",
                           "handler":"static_response"
                        }
                     ]
                  }
               ]
            },
            "srv1":{
               "listen":[
                  ":9090"
               ],
               "routes":[
                  {
                     "handle":[
                        {
                           "body":"Hello, world from 9090!",
                           "handler":"static_response"
                        }
                     ]
                  }
               ]
            }
         }
      }
   }
}

Making calls to those ports will produce these results:

~ ❯ curl http://localhost:8080
Hello, world from 8080!

~ ❯ curl http://localhost:9090
Hello, world from 9090!

The curious part is when I make a call to port 80, Caddy’s default HTTP port. The response is as below:

~ ❯ curl localhost:80
Hello, world from 8080!

Caddy responds with the same content as the one defined in the block corresponding to port :8080. About this behavior, I have several questions.

  • Why does Caddy behave this way?
  • Looking at the configuration in JSON, it can be seen that the configuration of port :8080 is the first one, the one corresponding to srv0. Does this mean that Caddy, by default, unless there is an explicit configuration for port :80 (HTTP port), is going to serve on port :80 the first configuration that is defined in the Caddyfile, that is, the srv0?
  • If I want to prevent this from happening and block those responses from port :80, how could I do it? This applies to more complete configurations as well. The case that someone tries to access Caddy through port :80 using the server’s IP comes to mind. Could that be blocked?

Thank you very much.

2. Error messages and/or full log output:

N/A

3. Caddy version:

v2.8.4 running in docker container

4. How I installed and ran Caddy:

Using docker with the following image:

  • caddy:latest

a. System environment:

  • OS: Ubuntu 24.04
  • Docker Version: `Docker version 27.3.1, build ce12230``

b. Command:

docker compose up

c. docker-compose.yml:

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - 80:80
      - 8080:8080
      - 9090:9090
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile

d. Caddyfile:

:8080 {
  respond "Hello, world from 8080!"
}

:9090 {
  respond "Hello, world from 9090!"
}

5. Links to relevant resources:

N/A

I’m confused. I don’t see that behaviour. Please show your Caddy logs, it should show if it’s setting up a server on port 80.

Caddy would set up a server on port 80 via the Automatic HTTPS feature if it detects that any of your servers are eligible for HTTPS (e.g. have host matchers produced from the site blocks), but your config is only setting up HTTP on non-HTTPS ports so it doesn’t make sense for it to set up port 80, and neither does it make sense that it would route connections on port 80 to your :8080 server.

Hello, @francislavoie.

Please find attached the Caddy Server logs.

caddy  | {"level":"info","ts":1733136748.2764683,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy  | {"level":"info","ts":1733136748.2769032,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy  | {"level":"warn","ts":1733136748.2769103,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
caddy  | {"level":"info","ts":1733136748.2773345,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy  | {"level":"debug","ts":1733136748.277402,"logger":"http.auto_https","msg":"adjusted config","tls":{"automation":{"policies":[{}]}},"http":{"servers":{"srv0":{"listen":[":8080"],"routes":[{"handle":[{"body":"Hello, world from 8080!","handler":"static_response"}]}],"automatic_https":{}},"srv1":{"listen":[":9090"],"routes":[{"handle":[{"body":"Hello, world from 9090!","handler":"static_response"}]}],"automatic_https":{}}}}}
caddy  | {"level":"info","ts":1733136748.2775416,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x40006ab380"}
caddy  | {"level":"debug","ts":1733136748.2776294,"logger":"http","msg":"starting server loop","address":"[::]:8080","tls":false,"http3":false}
caddy  | {"level":"info","ts":1733136748.2776458,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy  | {"level":"debug","ts":1733136748.2780273,"logger":"http","msg":"starting server loop","address":"[::]:9090","tls":false,"http3":false}
caddy  | {"level":"info","ts":1733136748.2780368,"logger":"http.log","msg":"server running","name":"srv1","protocols":["h1","h2","h3"]}
caddy  | {"level":"info","ts":1733136748.2841613,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy  | {"level":"info","ts":1733136748.2841685,"msg":"serving initial configuration"}
caddy  | {"level":"info","ts":1733136748.2856035,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"}
caddy  | {"level":"info","ts":1733136748.2856598,"logger":"tls","msg":"finished cleaning storage units"}

This behavior only occurs when configuring Caddy using Docker. If I directly use the Caddy binary in my OS, port 80 does not listen for requests. On the other hand, if using Docker, if I make requests to port 80, despite not being configured, Caddy responds with the response configured for srv0 (which in this case is the 8080 server). I have also tried to make the call inside the Docker container of Caddy Server and the response is the same. Have you tested it using Docker?

Attached is a video showing the behavior: [Caddy Server] Issue #26642 · CleanShot Cloud

Thanks in advance!

What’s orb debug caddy? I don’t use that.

I cannot replicate it. See below:

$ cat Caddyfile
{
	debug
}

:8080 {
	respond "Hello, world from 8080!"
}

:9090 {
	respond "Hello, world from 9090!"
}

$ cat compose.yml 
services:
  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - 9090:9090
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile

$ docker compose up -d
[+] Running 1/1
 ✔ Container caddy  Started                                                                                             0.4s 

$ docker compose exec caddy /bin/ash
/srv # apk add curl
<snip>

/srv # curl -v http://localhost:80
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:80...
* connect to ::1 port 80 from ::1 port 53308 failed: Connection refused
*   Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 from 127.0.0.1 port 58464 failed: Connection refused
* Failed to connect to localhost port 80 after 0 ms: Could not connect to server
* closing connection #0
curl: (7) Failed to connect to localhost port 80 after 0 ms: Could not connect to server

/srv # curl -v http://localhost:9090
* Host localhost:9090 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9090...
* Connected to localhost (::1) port 9090
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/8.11.0
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Date: Wed, 04 Dec 2024 05:55:16 GMT
< Content-Length: 23
< 
* Connection #0 to host localhost left intact
Hello, world from 9090!

This isn’t a problem with Caddy, your orb debug thing must be doing some extra stuff which produces that behaviour. We can’t help you with that.

Hello @francislavoie,

Indeed, you were right. I just tested the same configuration on a Linux server and Caddy’s behavior is as you describe.

What was happening to me is that on my personal computer I am using Orbstack, which is an alternative tool to Docker Desktop specific and optimized for macOS. Apparently this tool sets up an intermediate proxy that does this kind of shenanigans. orb debug <container_name> is not causing the problem. This command is used to have a set of debugging tools inside the shell of a container. The original cause is Orbstack itself.

Sorry for the inconvenience.

Thank you very much.

Best regards!

2 Likes