Repository: caddyserver/caddy (language: go)

Issue:
HTTP3 + `strict_sni_host` always results in `403` status
I use `curl -I --http3 https://localhost/index`

when strict_sni_host off, status is 200 from reverse_proxy
when strict_sni_host on, status is 403 from caddy

my Caddyfile:

```
{
	auto_https disable_redirects

	servers {
		protocol {
			experimental_http3
			strict_sni_host
		}
	}
}

localhost {
	reverse_proxy localhost:3000
}
```




Discussion / hints:
What's in your logs? Caddy will log an error when SNI does not match the HTTP Host. It will read something like `"strict host matching: TLS ServerName (%s) and HTTP Host (%s) values differ"`.

https://github.com/caddyserver/caddy/blob/a056fcd7ba64b6f313f08ec7a48536c726ca432b/modules/caddyhttp/server.go#L280-L299
I use `caddy run`, and use Caddyfaile

```
{
    debug
    auto_https disable_redirects

	servers {
		protocol {
			experimental_http3
			strict_sni_host
		}
	}
       
    log {
        output stderr
    }
}

localhost {
	reverse_proxy localhost:3000
}
```

but no any logs output for this request

header:
```
HTTP/3 403
server: Caddy
alt-svc: h3=":443"; ma=2592000,h3-34=":443"; ma=2592000,h3-32=":443"; ma=2592000,h3-29=":443"; ma=2592000
```

@francislavoie 

Try adding this to your site block:

```
handle_errors {
	respond "Code:{http.error.status_code} StatusText:{http.error.status_text} Message:{http.error.message}"
}
```

I think the error is being passed through the error handler chain, which ends up dropping the error message without being logged by default 🤔 
```
localhost {
         handle_errors {
		respond "Code:{http.error.status_code} StatusText:{http.error.status_text} Message:{http.error.message}"
	}

	reverse_proxy localhost:3000
}
```

still nothing output, can you test it?

in addition, how can I use http3 only without lowering to http/2?

I don't have a build of `curl` with HTTP/3 for Windows right now. I was able to get Firefox to connect over HTTP/3 one time, but it seems to want to pin itself to HTTP/2 😞 

I think the reason `handle_errors` isn't printing anything is because the error route has a `localhost` host matcher (see the output of `caddy adapt --pretty` to see what I mean) when using the Caddyfile, so to get around that I'm trying to use this JSON config with the matcher removed to see if I can get the error to appear:

```json
{
  "logging": {
    "logs": {
      "default": {
        "writer": {
          "output": "stderr"
        },
        "level": "DEBUG"
      }
    }
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "match": [
                {
                  "host": [
                    "localhost"
                  ]
                }
              ],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "body": "Foo",
                          "handler": "static_response"
                        }
                      ]
                    }
                  ]
                }
              ],
              "terminal": true
            }
          ],
          "errors": {
            "routes": [
              {
                "handle": [
                  {
                    "handler": "subroute",
                    "routes": [
                      {
                        "handle": [
                          {
                            "body": "Code:{http.error.status_code} StatusText:{http.error.status_text} Message:{http.error.message}",
                            "handler": "static_response"
                          }
                        ]
                      }
                    ]
                  }
                ],
                "terminal": true
              }
            ]
          },
          "automatic_https": {
            "disable_redirects": true
          },
          "strict_sni_host": true,
          "experimental_http3": true
        }
      }
    }
  }
}
```

Run Caddy with `caddy run --config caddy.json` with the above, and try again. Hopefully that should get further.
😢still nothing output

```
~ /usr/local/opt/curl/bin/curl -I --http3 https://localhost
HTTP/3 403
server: Caddy
alt-svc: h3=":443"; ma=2592000,h3-34=":443"; ma=2592000,h3-32=":443"; ma=2592000,h3-29=":443"; ma=2592000
```


> 😢still nothing output
> 
> ```
> ~ /usr/local/opt/curl/bin/curl -I --http3 https://localhost
> HTTP/3 403
> server: Caddy
> alt-svc: h3=":443"; ma=2592000,h3-34=":443"; ma=2592000,h3-32=":443"; ma=2592000,h3-29=":443"; ma=2592000
> ```

NEW FOUND:
```
/usr/local/opt/curl/bin/curl  --http3 https://localhost 
Code:403 StatusText:Forbidden Message:{http.error.message}
```
Alright welp, that wasn't too useful.

I guess next thing to try, if you don't mind making some code changes and building Caddy, you can add this just before the `fmt.Errorf` line in `modules/caddyhttp/server.go` to force Caddy to print out that message:

```go
fmt.Printf("strict host matching: TLS ServerName (%s) and HTTP Host (%s) values differ", r.TLS.ServerName, hostname)
```

Building Caddy is easy, just clone the repo, `cd cmd/caddy` then run `go build`. Then run with `./caddy run` etc.
> Alright welp, that wasn't too useful.
> 
> I guess next thing to try, if you don't mind making some code changes and building Caddy, you can add this just before the `fmt.Errorf` line to force Caddy to print out that message:
> 
> ```go
> fmt.Printf("strict host matching: TLS ServerName (%s) and HTTP Host (%s) values differ", r.TLS.ServerName, hostname)
> ```
> 
> Building Caddy is easy, just clone the repo, `cd cmd/caddy` then run `go build`. Then run with `./caddy run` etc.

🤣 I download binary from host directly without sources
Interesting, while scanning through our old issues, I found https://github.com/caddyserver/caddy/issues/4097 which is the same issue (`strict_sni_host` is implicitly enabled when client auth is enabled).
With caddy config
``` json
{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "strict_sni_host": true,
          "experimental_http3": true,
          "automatic_https": {
            "disable_redirects": true
          },
          "listen": [
            ":443"
          ],
          "logs": {},
          "errors": {
            "routes": [
              {
                "handle": [
                  {
                    "body": "{http.error}",
                    "handler": "static_response"
                  }
                ],
                "terminal": true
              }
            ]
          },
          "routes": [
            {
              "handle": [
                {
                  "body": "hello,world!\n",
                  "handler": "static_response"
                }
              ],
              "match": [
                {
                  "host": [
                    "localhost"
                  ]
                }
              ],
              "terminal": true
            }
          ]
        }
      }
    }
  },
  "logging": {
    "logs": {
      "default": {
        "level": "DEBUG"
      }
    }
  }
}
```
The response is (client: quic-go)
```
{id=3vhxga5h9} caddyhttp.(*Server).enforcementHandler (server.go:292): HTTP 403: strict host matching: TLS ServerName () and HTTP Host (localhost) values differ
```
In caddy log, `server_name` of the request is also missing
```json
{"request": {"remote_addr": "127.0.0.1:61399", "proto": "HTTP/3", "method": "GET", "host": "localhost:443", "uri": "/", "headers": {"Accept-Encoding": ["gzip"], "User-Agent": ["quic-go HTTP/3"]}, "tls": {"resumed": false, "version": 0, "cipher_suite": 0, "proto": "", "proto_mutual": true, "server_name": ""}}
```
Thanks @xiruizhao! That's helpful. I don't know why SNI would be empty though, that's pretty strange.

@marten-seemann I hate to always call you for help when it's HTTP/3 related :sweat_smile: but does this tell you anything? Could you think of a reason why SNI would be empty for HTTP/3 requests but not for HTTP/2 etc? Is Caddy doing something wrong that would prevent that from being kept?
This is probably due to the fact that we can't fill the `tls.ConnectionState` on HTTP/3 requests, see https://github.com/lucas-clemente/quic-go/issues/2879. Any idea how to best proceed here?
Hmm. Should we just skip `strict_sni_host` for HTTP/3 then? 🤔

What do you think @mholt?
We should probably _not_ skip it because it's a security feature:

https://github.com/caddyserver/caddy/blob/ce5a45db458166ff71cb776d70fd63e28196aafa/modules/caddyhttp/app.go#L170-L185

But I have no idea what to say otherwise, I don't understand HTTP/3 well enough to suggest anything :disappointed:  
The right fix would be to fix `crypto/tls`, such that one can call `ConnectionState()` at any time during the handshake. Unfortunately, that's not an easy thing to do.

Alternatively, we could fill in a fake `tls.ConnectionState`, that at least sets the `ServerName`.
What parts of `ConnectionState` aren't necessarily ready yet? Are `PeerCertificates` going to be in there by that point?

The code in question in Caddy is here:

https://github.com/caddyserver/caddy/blob/a056fcd7ba64b6f313f08ec7a48536c726ca432b/modules/caddyhttp/server.go#L280-L299

So clearly `r.TLS` is set, so there is a ConnectionState but it just seems incomplete?
No, `PeerCertificates` would be something that we'd need to get from crypto/tls.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 40b8d0ba465..ba074198e5e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [ ubuntu-latest, macos-latest, windows-latest ]
-        go: [ '1.18', '1.19' ]
+        go: [ '1.18', '1.20' ]
 
         include:
         # Set the minimum Go patch version for the given Go minor
@@ -27,8 +27,8 @@ jobs:
         - go: '1.18'
           GO_SEMVER: '~1.18.4'
 
-        - go: '1.19'
-          GO_SEMVER: '~1.19.0'
+        - go: '1.20'
+          GO_SEMVER: '~1.20.0'
 
         # Set some variables per OS, usable via ${{ matrix.VAR }}
         # CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml
index e8d07980898..8b5e429a717 100644
--- a/.github/workflows/cross-build.yml
+++ b/.github/workflows/cross-build.yml
@@ -16,13 +16,13 @@ jobs:
       fail-fast: false
       matrix:
         goos: ['android', 'linux', 'solaris', 'illumos', 'dragonfly', 'freebsd', 'openbsd', 'plan9', 'windows', 'darwin', 'netbsd']
-        go: [ '1.19' ]
+        go: [ '1.20' ]
 
         include:
         # Set the minimum Go patch version for the given Go minor
         # Usable via ${{ matrix.GO_SEMVER }}
-        - go: '1.19'
-          GO_SEMVER: '~1.19.0'
+        - go: '1.20'
+          GO_SEMVER: '~1.20.0'
 
     runs-on: ubuntu-latest
     continue-on-error: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 955e8d86913..d8ea96ba203 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,13 +11,13 @@ jobs:
     strategy:
       matrix:
         os: [ ubuntu-latest ]
-        go: [ '1.19' ]
+        go: [ '1.20' ]
 
         include:
         # Set the minimum Go patch version for the given Go minor
         # Usable via ${{ matrix.GO_SEMVER }}
-        - go: '1.19'
-          GO_SEMVER: '~1.19.0'
+        - go: '1.20'
+          GO_SEMVER: '~1.20.0'
 
     runs-on: ${{ matrix.os }}
     # https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
diff --git a/go.mod b/go.mod
index c329bbdfb2a..b762aee6e72 100644
--- a/go.mod
+++ b/go.mod
@@ -14,9 +14,9 @@ require (
 	github.com/google/uuid v1.3.0
 	github.com/klauspost/compress v1.15.11
 	github.com/klauspost/cpuid/v2 v2.1.2
-	github.com/lucas-clemente/quic-go v0.31.0
 	github.com/mholt/acmez v1.0.4
 	github.com/prometheus/client_golang v1.12.2
+	github.com/quic-go/quic-go v0.32.0
 	github.com/smallstep/certificates v0.23.0
 	github.com/smallstep/nosql v0.5.0
 	github.com/smallstep/truststore v0.12.0
@@ -30,7 +30,7 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0
 	go.opentelemetry.io/otel/sdk v1.4.0
 	go.uber.org/zap v1.23.0
-	golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
+	golang.org/x/crypto v0.4.0
 	golang.org/x/net v0.5.0
 	golang.org/x/term v0.4.0
 	google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c
@@ -45,9 +45,13 @@ require (
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
 	github.com/onsi/ginkgo/v2 v2.2.0 // indirect
+	github.com/quic-go/qpack v0.4.0 // indirect
+	github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
+	github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
+	github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
 	github.com/rogpeppe/go-internal v1.9.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
-	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
+	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
 )
 
 require (
@@ -90,9 +94,6 @@ require (
 	github.com/jackc/pgx/v4 v4.17.2 // indirect
 	github.com/libdns/libdns v0.2.1 // indirect
 	github.com/manifoldco/promptui v0.9.0 // indirect
-	github.com/marten-seemann/qpack v0.3.0 // indirect
-	github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
-	github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
 	github.com/mattn/go-colorable v0.1.8 // indirect
 	github.com/mattn/go-isatty v0.0.13 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
@@ -127,10 +128,10 @@ require (
 	go.step.sm/linkedca v0.19.0 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+	golang.org/x/mod v0.6.0 // indirect
 	golang.org/x/sys v0.4.0
 	golang.org/x/text v0.6.0 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	golang.org/x/tools v0.2.0 // indirect
 	google.golang.org/grpc v1.50.1 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
diff --git a/go.sum b/go.sum
index 55f673b31e0..bc565631fc9 100644
--- a/go.sum
+++ b/go.sum
@@ -434,18 +434,10 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
 github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
-github.com/lucas-clemente/quic-go v0.31.0 h1:MfNp3fk0wjWRajw6quMFA3ap1AVtlU+2mtwmbVogB2M=
-github.com/lucas-clemente/quic-go v0.31.0/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
 github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
-github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
-github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
-github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
-github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
-github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -568,6 +560,16 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
 github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
+github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
+github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
+github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
+github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
+github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
+github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
+github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
+github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
+github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -757,8 +759,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
-golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
+golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -769,8 +771,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
-golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
+golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
+golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -792,8 +794,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
 golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -858,8 +861,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -996,8 +999,9 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/listeners.go b/listeners.go
index ea03ca421b2..5bf85a08532 100644
--- a/listeners.go
+++ b/listeners.go
@@ -30,8 +30,8 @@ import (
 	"syscall"
 	"time"
 
-	"github.com/lucas-clemente/quic-go"
-	"github.com/lucas-clemente/quic-go/http3"
+	"github.com/quic-go/quic-go"
+	"github.com/quic-go/quic-go/http3"
 	"go.uber.org/zap"
 )
 
@@ -462,7 +462,7 @@ func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (
 	// of closes) because closing the quic.EarlyListener doesn't actually close
 	// the underlying PacketConn, but we need to for unix sockets since we dup
 	// the file descriptor and thus need to close the original; track issue:
-	// https://github.com/lucas-clemente/quic-go/issues/3560#issuecomment-1258959608
+	// https://github.com/quic-go/quic-go/issues/3560#issuecomment-1258959608
 	var unix *unixConn
 	if uc, ok := ln.(*unixConn); ok {
 		unix = uc
diff --git a/modules/caddyhttp/app.go b/modules/caddyhttp/app.go
index d790284c852..da25d372494 100644
--- a/modules/caddyhttp/app.go
+++ b/modules/caddyhttp/app.go
@@ -574,7 +574,7 @@ func (app *App) Stop() error {
 
 		// TODO: we have to manually close our listeners because quic-go won't
 		// close listeners it didn't create along with the server itself...
-		// see https://github.com/lucas-clemente/quic-go/issues/3560
+		// see https://github.com/quic-go/quic-go/issues/3560
 		for _, el := range server.h3listeners {
 			if err := el.Close(); err != nil {
 				app.logger.Error("HTTP/3 listener close",
@@ -583,7 +583,7 @@ func (app *App) Stop() error {
 			}
 		}
 
-		// TODO: CloseGracefully, once implemented upstream (see https://github.com/lucas-clemente/quic-go/issues/2103)
+		// TODO: CloseGracefully, once implemented upstream (see https://github.com/quic-go/quic-go/issues/2103)
 		if err := server.h3server.Close(); err != nil {
 			app.logger.Error("HTTP/3 server shutdown",
 				zap.Error(err),
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index c9cc9e6d96e..c497dc7a1d2 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -232,7 +232,7 @@ func SanitizedPathJoin(root, reqPath string) string {
 		root = "."
 	}
 
-	path := filepath.Join(root, filepath.Clean("/"+reqPath))
+	path := filepath.Join(root, path.Clean("/"+reqPath))
 
 	// filepath.Join also cleans the path, and cleaning strips
 	// the trailing slash, so we need to re-add it afterwards.
diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go
index f50abf0e35a..420cc4a018d 100644
--- a/modules/caddyhttp/server.go
+++ b/modules/caddyhttp/server.go
@@ -33,8 +33,8 @@ import (
 	"github.com/caddyserver/caddy/v2/modules/caddyevents"
 	"github.com/caddyserver/caddy/v2/modules/caddytls"
 	"github.com/caddyserver/certmagic"
-	"github.com/lucas-clemente/quic-go"
-	"github.com/lucas-clemente/quic-go/http3"
+	"github.com/quic-go/quic-go"
+	"github.com/quic-go/quic-go/http3"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 )


Test patch: