feat(security): trust X-Forwarded-Proto behind reverse proxy

Adds server.forward-headers-strategy: native so that Jetty honours
X-Forwarded-{Proto,For,Host} from Caddy. Without this, getScheme(),
redirect URLs, and Spring Session "Secure" cookies reflect the
internal http hop instead of the original https client request.

Refs #497.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-10 21:33:39 +02:00
parent 80ccc0f3c6
commit 83f022ff4b
2 changed files with 43 additions and 0 deletions

View File

@@ -38,6 +38,12 @@ spring:
starttls:
enable: true
server:
# Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that
# request.getScheme(), redirect URLs, and Spring Session "Secure" cookies
# reflect the original https client request, not the http hop from Caddy.
forward-headers-strategy: native
management:
health:
mail:

View File

@@ -0,0 +1,37 @@
package org.raddatz.familienarchiv.config;
import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.PostgresContainerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import software.amazon.awssdk.services.s3.S3Client;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("test")
@Import(PostgresContainerConfig.class)
class ForwardHeadersConfigurationTest {
@MockitoBean
S3Client s3Client;
@Autowired
@Value("${server.forward-headers-strategy:}")
String forwardHeadersStrategy;
@Test
void forward_headers_strategy_is_native_for_reverse_proxy_deployment() {
// Caddy terminates TLS and forwards X-Forwarded-Proto: https.
// Spring must trust those headers so that AppUser-facing redirect URLs,
// Spring Session cookies (Secure flag), and HttpServletRequest.getScheme()
// reflect the original client-facing scheme rather than the internal http hop.
assertThat(forwardHeadersStrategy)
.as("server.forward-headers-strategy must be 'native' so Jetty honours X-Forwarded-Proto behind Caddy")
.isEqualTo("native");
}
}