Ah, I see.
Okay, two things:
First, Caddy’s default response - if nothing else matches - is a blank http/200.
There has been a new addition to the wiki lately about that, see Why Caddy emits empty 200 OK responses by default if you want to know why.
The scope of possible sites to match is per port, essentially.
Your die-lordis.de
is on :443
, because of Automatic HTTPS — Caddy Documentation.
http://localhost:2020
on the other hand, is on port :2020
, like you specified.
Now, if you connect via http://localhost:2020, then your page will show.
That’s because Caddy matches your site blocks based on the Host
header.
Essentially, your client (in your case, most likely your web browser) sends the Host: localhost:2020
to Caddy and Caddy is like “yee, I understand! You want me to serve that http://localhost:2020
you specified in the Caddyfile to you!”.
But if you would go ahead and send a custom Host
header, like Host: example.com
, then Caddy would be like “eeeehh, I know you are trying to connect to me and all, cool, I’ll permit that, but I don’t know that page! Here, see my default - the blank http/200 - instead”.
Take the following example Caddyfile:
http://localhost:2020 {
respond "example response"
}
and curl
:
❯ curl http://localhost:2020 -iv
* Trying 127.0.0.1:2020...
* Connected to localhost (127.0.0.1) port 2020 (#0)
> GET / HTTP/1.1
> Host: localhost:2020
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
< Server: Caddy
Server: Caddy
< Date: Mon, 21 Nov 2022 08:06:32 GMT
Date: Mon, 21 Nov 2022 08:06:32 GMT
< Content-Length: 16
Content-Length: 16
<
* Connection #0 to host localhost left intact
example response%
You can read the >
lines as curl
sending something to Caddy, and the <
ones as curl
receiving something.
Note the > Host: localhost:2020
.
The same config, this time with a custom Host:
header:
❯ curl http://localhost:2020 -iv --header "Host: example.com"
* Trying 127.0.0.1:2020...
* Connected to localhost (127.0.0.1) port 2020 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: Caddy
Server: Caddy
< Date: Mon, 21 Nov 2022 08:09:39 GMT
Date: Mon, 21 Nov 2022 08:09:39 GMT
< Content-Length: 0
Content-Length: 0
<
* Connection #0 to host localhost left intact
No “example response” as response and a Content-Length
of 0 bytes.
That’s the default response, if nothing matches, as explained in the wiki linked above.
Second, the reverse_proxy (Caddyfile directive) — Caddy Documentation passes multiple headers to the upstream (in your case http://localhost:2020
) by default.
Quote:
Defaults
By default, Caddy passes thru incoming headers—including
Host
—to the backend without modifications, with three exceptions:
There is that Host header again!
So essentially, what’s happening is:
- Client sends
Host: die-lordis.de
to:443
- Caddy is like “yee, alright, great, I’ll pass that to
http://localhost:2020
as instructed” - Caddy requests
http://localhost:2020
with the original (unchanged)Host: die-lordis.de
- Caddy has no
die-lordis.de
configured on port:2020
and default to the blank http/200
There are multiple ways to fix that:
Option 1: Override the Host header within the reverse_proxy directive:
reverse_proxy http://localhost:2020 {
header_up Host {upstream_hostport}
}
or
reverse_proxy http://localhost:2020 {
header_up Host localhost:2020
}
Both achieve literally the same. Though the first one leverages Caddyfile Concepts — Caddy Documentation, so you have a single source of truth
Option 2: Use a catch-all for port :2020
You could also just make port :2020
not care about the Host
header. A catch-all essentially.
For that, you would just drop the localhost
in your config like so:
http://:2020 {
root * /var/www/html
file_server
}
Option 3: Specify your domain within the same block
The Caddyfile allows you to specify multiple domains/hostnames/IPs in a single block.
You can either separate them by a comma (,
) or a whitespace (
).
die-lordis.de http://localhost:2020 {
root * /var/www/html
file_server
}
Valid site addresses are mentioned at Caddyfile Concepts — Caddy Documentation in case you want to see some more
One more thing:
If you had done something like
http://example.localhost {
reverse_proxy http://localhost
}
http://localhost {
respond "example response"
}
and then tried to connect to http://example.localhost
, you would have ended up in an infinite loop, because Caddy keeping the Host
header unchanged would never reach http://localhost
but instead http://example.localhost
, which would then in turn connect to http://example.localhost
over and over again.
Hope that explains why you are experiencing the issue you are having and some workarounds