Skip to content

Recommend disabling context path redirects when using proxy-terminated SSL with Tomcat #22908

@TheSmoun

Description

@TheSmoun

Affects: Spring Boot v2.2.1.RELEASE


context path redirect causes protocol downgrade to http

I'm trying to run a basic Spring Boot application behind a proxy and with a context path, because later it will run on a server with other applications and inside a Docker container.
I'm having difficulties with the redirect to the context path. Here is a structural overview of the system:

+--------+  https  +-------+  http   +--------+
| Client | ------> | Proxy | ------> | Spring |
+--------+         +-------+         +--------+

When sending a request with the schema https://<proxy-url>/<context-path>, the Spring redirects the client to http://<proxy-url>/<context-path>/. So it downgrades the protocol from https to http.

I honestly don't know whether this is a bug or intended behavior. Is there a way to make Spring redirect to https instead of http. The proxy redirect contains the correct X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port headers.

The Curl output below shows that Spring reads the headers properly when adding the slash at the end of the context path. The Curl requests talk directly to Spring and I did set the headers manually to emulate the proxy. I also attached the code of the test application and a screenshot of the redirect within Chrome.

Request and redirect screenshot

redirect

Curl

Slash at the end of the context path

C:\Users\<user>>curl -v -H "X-Forwarded-Proto: https" -H "X-Forwarded-Port: 443" -H "X-Forwarded-Host: <host>" http://localhost:8081/hweproxy/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8081 (#0)
> GET /hweproxy/ HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.55.1
> Accept: */*
> X-Forwarded-Proto: https
> X-Forwarded-Port: 443
> X-Forwarded-Host: <host>
>
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 33
< Date: Tue, 11 Aug 2020 09:32:52 GMT
<
https://<host>/hweproxy/* Connection #0 to host localhost left intact

No slash at the end of the context path

C:\Users\<user>>curl -v -H "X-Forwarded-Proto: https" -H "X-Forwarded-Port: 443" -H "X-Forwarded-Host: <host>" http://localhost:8081/hweproxy
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8081 (#0)
> GET /hweproxy HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.55.1
> Accept: */*
> X-Forwarded-Proto: https
> X-Forwarded-Port: 443
> X-Forwarded-Host: <host>
>
< HTTP/1.1 302
< Location: http://localhost:8081/hweproxy/
< Transfer-Encoding: chunked
< Date: Tue, 11 Aug 2020 09:19:51 GMT
<
* Connection #0 to host localhost left intact

Basic Spring Boot application code

Java Code

@SpringBootApplication
public class SpringPoC {

    public static void main(String[] args) {
        SpringApplication.run(SpringPoC.class, args);
    }
}

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("**").permitAll()

            .and()
            .cors()

            .and()
            .csrf()
            .disable();
    }
}

@RestController
@RequestMapping("/")
public class DummyController {
    
    @GetMapping
    public ResponseEntity<?> dummyEndpoint(HttpServletRequest req) {
        return ResponseEntity.ok(req.getRequestURL());
    }
}

application.properties

server.port = 8081
server.servlet.context-path = /hweproxy
server.forward-headers-strategy = FRAMEWORK
server.tomcat.remoteip.internal-proxies = \\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>spring-poc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Spring Prove of Concept</name>
    <url>http://example.com</url>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <build>
        <sourceDirectory>src/main/java</sourceDirectory>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>
</project>

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions