I’m having some trouble getting Caddy to serve redirects for HTTPs correctly when mixing wildcard domains, and HTTP only websites.
1. The problem I’m having:
I have a Caddyfile
that will serve three things:
- A healthcheck endpoint on a non-standard port, on
localhost
- An internal only service on an HTTP port, on an internal domain
- A number of externally accessible subdomains on HTTPS, served with
on_demand
TLS and a wildcard DNS certificate, on a second external domain
I am able to get this configuration to work, except that the externally accessible domains do not issue 301 redirects from the HTTP endpoint.
2. Error messages and/or full log output:
I can hit the external domain on HTTPS as expected:
curl -v https://rpc.external-example.com/
* Host rpc.external-example.com:443 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
* Trying x.x.x.x:443...
* Connected to rpc.external-example.com (x.x.x.x) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
* subject: CN=rpc.external-example.com
* start date: Jun 27 23:18:41 2024 GMT
* expire date: Sep 25 23:18:40 2024 GMT
* subjectAltName: host "rpc.external-example.com" matched cert's "rpc.external-example.com"
* issuer: C=US; O=Let's Encrypt; CN=E6
* SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://rpc.external-example.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: rpc.external-example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: rpc.external-example.com
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/2 200
< content-type: application/json
< date: Sun, 30 Jun 2024 21:16:42 GMT
< server: Caddy
[snip]
However, HTTP requests error, and I’d expect them to issue an HTTP 301 redirect:
curl -v http://rpc.external-example.com/
* Host rpc.external-example.com:80 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
* Trying x.x.x.x:80...
* Connected to rpc.external-example.com (x.x.x.x) port 80
> GET / HTTP/1.1
> Host: rpc.external-example.com
> User-Agent: curl/8.6.0
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection
curl: (56) Recv failure: Connection reset by peer
For completeness, the other endpoints are working over HTTP just fine:
Healthcheck works as expected:
curl -v 127.0.0.1:8080/healthz
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /healthz HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Date: Sun, 30 Jun 2024 21:28:52 GMT
< Content-Length: 7
<
* Connection #0 to host 127.0.0.1 left intact
health
Internal site also works as expected:
curl -v rpc.internal-example.com/
* Host rpc.internal-example.com:80 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
* Trying x.x.x.x:80...
* Connected to rpc.internal-example.com (x.x.x.x) port 80
> GET / HTTP/1.1
> Host: rpc.internal-example.com
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 863924
< Content-Type: text/html; charset=utf-8
< Date: Sun, 30 Jun 2024 21:14:06 GMT
< Server: Caddy
[snip]
3. Caddy version:
Caddy: tags/v2.8.4
XCaddy: tags/v0.4.2
4. How I installed and ran Caddy:
Build xcaddy:
go build -trimpath -o ./ ./...
Build Caddy:
xcaddy build v2.8.4
--output ./caddy
--with github.com/caddy-dns/route53
Running Caddy:
a. System environment:
Ubuntu Jammy
b. Command:
Simply on the CLI for demoing:
./caddy run --config ./Caddyfile --environ --watch
c. Service/unit/compose file:
N/A
d. My complete Caddy config:
# Global Options
{
# Enable http/https and cleartext grpc
servers {
protocols h1 h2 h2c
}
log default {
output stdout
format console
}
# Request on demand
# For demo purposes, this always returns 200.
on_demand_tls {
ask http://rpc.internal-example.com/v1/caddy/ask
}
}
# An internal service, served on http
http://rpc.internal-example.com {
# Try to dynamically serve a service from Consul, with a static fallback
reverse_proxy {
dynamic srv {
name rpc-internal.service.consul
refresh 15s
}
to http://internal.ts.net:9090
lb_policy random
}
}
# Heath check, which is served on localhost over HTTP
# Inspired by: https://caddy.community/t/health-check-endpoint-to-monitor-caddy-itself/1517
http://127.0.0.1:8080 {
handle /healthz {
respond "healthy"
}
}
# External services
*.external-example.com.com {
tls {
# Use Route53 DNS for ACME
dns route53 {
max_retries 10
access_key_id "<redacted>"
secret_access_key "<redacted>"
region "<redacted>"
}
# See: https://github.com/caddy-dns/route53/issues/43
propagation_timeout -1
propagation_delay 20s
# Use on demand TLS.
# Not strictly needed in this example, but needed in the production version.
on_demand
}
@external-rpc host rpc.external-example.com
handle @external-rpc {
reverse_proxy {
dynamic srv {
name rpc-external.service.consul
refresh 15s
}
lb_policy random
}
}
@external-rest host rest.external-example.com.com
handle @external-rest {
reverse_proxy {
dynamic srv {
name rest-external.service.consul
refresh 15s
}
lb_policy random
}
}
}