@@ -39,13 +39,16 @@ class_exists(ServerBag::class);
3939 */
4040class Request
4141{
42- const HEADER_FORWARDED = 0b00001 ; // When using RFC 7239
43- const HEADER_X_FORWARDED_FOR = 0b00010 ;
44- const HEADER_X_FORWARDED_HOST = 0b00100 ;
45- const HEADER_X_FORWARDED_PROTO = 0b01000 ;
46- const HEADER_X_FORWARDED_PORT = 0b10000 ;
47- const HEADER_X_FORWARDED_ALL = 0b11110 ; // All "X-Forwarded-*" headers
48- const HEADER_X_FORWARDED_AWS_ELB = 0b11010 ; // AWS ELB doesn't send X-Forwarded-Host
42+ const HEADER_FORWARDED = 0b000001 ; // When using RFC 7239
43+ const HEADER_X_FORWARDED_FOR = 0b000010 ;
44+ const HEADER_X_FORWARDED_HOST = 0b000100 ;
45+ const HEADER_X_FORWARDED_PROTO = 0b001000 ;
46+ const HEADER_X_FORWARDED_PORT = 0b010000 ;
47+ const HEADER_X_FORWARDED_PREFIX = 0b100000 ;
48+
49+ const HEADER_X_FORWARDED_ALL = 0b011110 ; // All "X-Forwarded-*" headers sent by "usual" reverse proxy
50+ const HEADER_X_FORWARDED_AWS_ELB = 0b011010 ; // AWS ELB doesn't send X-Forwarded-Host
51+ const HEADER_X_FORWARDED_TRAEFIK = 0b111110 ; // All "X-Forwarded-*" headers sent by Traefik reverse proxy
4952
5053 const METHOD_HEAD = 'HEAD ' ;
5154 const METHOD_GET = 'GET ' ;
@@ -237,6 +240,7 @@ class Request
237240 self ::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST ' ,
238241 self ::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO ' ,
239242 self ::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT ' ,
243+ self ::HEADER_X_FORWARDED_PREFIX => 'X_FORWARDED_PREFIX ' ,
240244 ];
241245
242246 /**
@@ -894,6 +898,24 @@ public function getBasePath()
894898 * @return string The raw URL (i.e. not urldecoded)
895899 */
896900 public function getBaseUrl ()
901+ {
902+ $ trustedPrefix = '' ;
903+
904+ // the proxy prefix must be prepended to any prefix being needed at the webserver level
905+ if ($ this ->isFromTrustedProxy () && $ trustedPrefixValues = $ this ->getTrustedValues (self ::HEADER_X_FORWARDED_PREFIX )) {
906+ $ trustedPrefix = rtrim ($ trustedPrefixValues [0 ], '/ ' );
907+ }
908+
909+ return $ trustedPrefix .$ this ->getBaseUrlReal ();
910+ }
911+
912+ /**
913+ * Returns the real base URL received by the webserver from which this request is executed.
914+ * The URL does not include trusted reverse proxy prefix.
915+ *
916+ * @return string The raw URL (i.e. not urldecoded)
917+ */
918+ private function getBaseUrlReal ()
897919 {
898920 if (null === $ this ->baseUrl ) {
899921 $ this ->baseUrl = $ this ->prepareBaseUrl ();
@@ -1910,7 +1932,7 @@ protected function preparePathInfo()
19101932 $ requestUri = '/ ' .$ requestUri ;
19111933 }
19121934
1913- if (null === ($ baseUrl = $ this ->getBaseUrl ())) {
1935+ if (null === ($ baseUrl = $ this ->getBaseUrlReal ())) {
19141936 return $ requestUri ;
19151937 }
19161938
@@ -2014,7 +2036,7 @@ private function getTrustedValues(int $type, string $ip = null): array
20142036 }
20152037 }
20162038
2017- if ((self ::$ trustedHeaderSet & self ::HEADER_FORWARDED ) && $ this ->headers ->has (self ::$ trustedHeaders [self ::HEADER_FORWARDED ])) {
2039+ if ((self ::$ trustedHeaderSet & self ::HEADER_FORWARDED ) && ( isset ( self :: $ forwardedParams [ $ type ])) && $ this ->headers ->has (self ::$ trustedHeaders [self ::HEADER_FORWARDED ])) {
20182040 $ forwarded = $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_FORWARDED ]);
20192041 $ parts = HeaderUtils::split ($ forwarded , ',;= ' );
20202042 $ forwardedValues = [];
0 commit comments