From 83f022ff4bbfd2c6f8fb0991ed47e2309557b313 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 10 May 2026 21:33:39 +0200 Subject: [PATCH] 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 --- backend/src/main/resources/application.yaml | 6 +++ .../ForwardHeadersConfigurationTest.java | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index 1cdd7673..6e12b9f6 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -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: diff --git a/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java new file mode 100644 index 00000000..b97f5ff0 --- /dev/null +++ b/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java @@ -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"); + } +}