Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 12:15 UTC
caddyhttp: prefer port 443 in auto-HTTPS and add tests (#7666)
Matt Holt committed
Apr 23, 2026 at 01:29 UTC
441d5eb0628c4a9fbe74c13eca6ab255056e3e57
3 files changed
+97
-7
caddytest/integration/autohttps_test.go
+22
@@ -55,6 +55,28 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T
55
tester.AssertRedirect("http://localhost:9080/", "https://localhost:1234/", http.StatusPermanentRedirect)
56
}
57
58
+func TestAutoHTTPtoHTTPSRedirectsPreferHTTPSPortOverAlternatePort(t *testing.T) {
59
+ tester := caddytest.NewTester(t)
60
+ tester.InitServer(`
61
+ {
62
+ skip_install_trust
63
+ admin localhost:2999
64
+ http_port 9080
65
+ https_port 9443
66
+ local_certs
67
+ }
68
+ localhost {
69
+ respond "Canonical"
70
+ }
71
+
72
+ localhost:10443 {
73
+ respond "Alternate"
74
+ }
75
+ `, "caddyfile")
76
+
77
+ tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
78
+}
79
+
80
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
81
tester := caddytest.NewTester(t)
82
tester.InitServer(`
modules/caddyhttp/autohttps.go
+31
-7
@@ -258,18 +258,13 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
258
// an empty string to indicate a catch-all, which we have to
259
// treat special later
260
if len(serverDomainSet) == 0 {
261
- redirDomains[""] = append(redirDomains[""], addr)
261
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "", addr)
262
continue
263
}
264
265
// ...and associate it with each domain in this server
266
for d := range serverDomainSet {
267
- // if this domain is used on more than one HTTPS-enabled
268
- // port, we'll have to choose one, so prefer the HTTPS port
269
- if _, ok := redirDomains[d]; !ok ||
270
- addr.StartPort == uint(app.httpsPort()) {
271
- redirDomains[d] = append(redirDomains[d], addr)
272
- }
267
+ app.recordAutoHTTPSRedirectAddress(redirDomains, d, addr)
268
}
269
}
270
}
@@ -517,6 +512,35 @@ redirServersLoop:
512
return nil
513
}
514
515
+// recordAutoHTTPSRedirectAddress stores redirect destinations for one domain
516
+// using a single winning port while keeping all bind addresses on that port.
517
+//
518
+// This is needed to avoid two opposite regressions in auto-HTTPS redirects:
519
+// preserve all listener addresses when a site binds multiple addresses on the
520
+// same HTTPS port, but do not mix in alternate HTTPS ports when the canonical
521
+// app HTTPS port is also available.
522
+func (app *App) recordAutoHTTPSRedirectAddress(redirDomains map[string][]caddy.NetworkAddress, domain string, addr caddy.NetworkAddress) {
523
+ existing := redirDomains[domain]
524
+ if len(existing) == 0 {
525
+ redirDomains[domain] = []caddy.NetworkAddress{addr}
526
+ return
527
+ }
528
+
529
+ existingPort := existing[0].StartPort
530
+ if addr.StartPort != existingPort {
531
+ if addr.StartPort == uint(app.httpsPort()) && existingPort != uint(app.httpsPort()) {
532
+ redirDomains[domain] = []caddy.NetworkAddress{addr}
533
+ }
534
+ return
535
+ }
536
+
537
+ if slices.Contains(existing, addr) {
538
+ return
539
+ }
540
+
541
+ redirDomains[domain] = append(existing, addr)
542
+}
543
+
544
func (app *App) makeRedirRoute(redirToPort uint, matcherSet MatcherSet) Route {
545
redirTo := "https://{http.request.host}"
546
modules/caddyhttp/autohttps_test.go
new
+44
@@ -0,0 +1,44 @@
1
+package caddyhttp
2
+
3
+import (
4
+ "testing"
5
+
6
+ "github.com/caddyserver/caddy/v2"
7
+)
8
+
9
+func TestRecordAutoHTTPSRedirectAddressPrefersHTTPSPort(t *testing.T) {
10
+ app := &App{HTTPSPort: 443}
11
+ redirDomains := make(map[string][]caddy.NetworkAddress)
12
+
13
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", StartPort: 2345, EndPort: 2345})
14
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", StartPort: 443, EndPort: 443})
15
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", StartPort: 8443, EndPort: 8443})
16
+
17
+ got := redirDomains["example.com"]
18
+ if len(got) != 1 {
19
+ t.Fatalf("expected 1 redirect address, got %d: %#v", len(got), got)
20
+ }
21
+ if got[0].StartPort != 443 {
22
+ t.Fatalf("expected redirect to prefer HTTPS port 443, got %#v", got[0])
23
+ }
24
+}
25
+
26
+func TestRecordAutoHTTPSRedirectAddressKeepsAllBindAddressesOnWinningPort(t *testing.T) {
27
+ app := &App{HTTPSPort: 443}
28
+ redirDomains := make(map[string][]caddy.NetworkAddress)
29
+
30
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", Host: "10.0.0.189", StartPort: 8443, EndPort: 8443})
31
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", Host: "10.0.0.189", StartPort: 443, EndPort: 443})
32
+ app.recordAutoHTTPSRedirectAddress(redirDomains, "example.com", caddy.NetworkAddress{Network: "tcp", Host: "2603:c024:8002:9500:9eb:e5d3:3975:d056", StartPort: 443, EndPort: 443})
33
+
34
+ got := redirDomains["example.com"]
35
+ if len(got) != 2 {
36
+ t.Fatalf("expected 2 redirect addresses for both bind addresses on the winning port, got %d: %#v", len(got), got)
37
+ }
38
+ if got[0].StartPort != 443 || got[1].StartPort != 443 {
39
+ t.Fatalf("expected both redirect addresses to stay on HTTPS port 443, got %#v", got)
40
+ }
41
+ if got[0].Host != "10.0.0.189" || got[1].Host != "2603:c024:8002:9500:9eb:e5d3:3975:d056" {
42
+ t.Fatalf("expected both bind addresses to be preserved, got %#v", got)
43
+ }
44
+}