diff --git a/src/LinkBuilder/LinkBuilder.php b/src/LinkBuilder/LinkBuilder.php index 1793e50..6febfab 100644 --- a/src/LinkBuilder/LinkBuilder.php +++ b/src/LinkBuilder/LinkBuilder.php @@ -38,6 +38,13 @@ public function asUrl(): string { $scheme = $this->_scheme ?? ($this->_request->isSecure(true) ? 'https' : 'http'); $port = $this->_port ?? $this->_request->port(); + + // When the scheme comes from a proxy that terminated tls, the port the + // request arrived on is the proxy's, not the one the url is reachable on + $schemeIsForwarded = $this->_scheme === null + && $this->_port === null + && $this->_request->isSecure(true) + && !$this->_request->isSecure(); return ($scheme . '://') . implode( '.', @@ -47,7 +54,7 @@ public function asUrl(): string ($this->_tld ?? $this->_request->tld()), ] ) - . ($this->_isStandardPort($this->_scheme ?? $this->_request->getScheme(), $port) ? '' : ':' . $port) + . ($schemeIsForwarded || $this->_isStandardPort($scheme, $port) ? '' : ':' . $port) . (isset($this->_path[0]) && $this->_path[0] !== '/' ? '/' : '') . $this->_path . (!empty($this->_query) ? '?' . http_build_query($this->_query) : null) diff --git a/tests/LinkBuilderTest.php b/tests/LinkBuilderTest.php index 4932140..544d0ba 100644 --- a/tests/LinkBuilderTest.php +++ b/tests/LinkBuilderTest.php @@ -83,4 +83,21 @@ public function testAsUrl() $lb->setFragment('def'); self::assertEquals('https://secure.packaged.local#def', $lb->asUrl()); } + + /** + * Behind a TLS terminating proxy the scheme comes from X-Forwarded-Proto, so + * 443 is the standard port and should not be written into the url + */ + public function testAsUrlBehindTlsTerminatingProxy() + { + $request = Request::create('http://www.packaged.local:443/'); + $request->headers->set('X_FORWARDED_PROTO', 'https'); + + self::assertFalse($request->isSecure()); + self::assertTrue($request->isSecure(true)); + self::assertEquals('http', $request->getScheme()); + self::assertEquals(443, $request->port()); + + self::assertEquals('https://www.packaged.local', LinkBuilder::fromRequest($request)->asUrl()); + } }