1. 문제점
DesisionLog 백엔드 서비스를 Docker 기반으로 배포하고, Nginx를 통해서 decision.ngup.kro.kr 도멘인으로 HTTP 접속이 가능하도록 구성했다. 아래 링크를 통해서 접속하도록 설정했다.
https://decisionlog.duckdns.org
DecisionLog
decisionlog.duckdns.org
하지만 실제 백엔드 애플리케이션은 Nginx 뒤에서 Docker 컨테이너로 실행되고 있으며, 내부에서는 다음 구조로 요청이 전달된다.
사용자 브라우저 -> Nginx-> Spring Boot Backend
이 과정에서 Spring Boot가 외부 요청을 HTTPS가 아닌 HTTP 요청으로 인식하는 문제가 발생했다.
또한 HTTPS 적용 전에는 HTTP 기반으로 CORS문제 없이 작동했기 때문에 그리고 fronted와 backend의 도메인이 다른걸 사용하도록 설정하지 않았기 때문에 Backend의 CORS 설정을 변경하라는 AI의 답변이 신뢰가 안갔다.
2. 발생한 문제
Nginx는 외부 HTTPS 요청을 받아서 SSL 인증서를 이용해 복호화한 뒤, 내부 Spring Boot 컨테이너로 HTTP 요청을 전달한다.
Nginx 설정은 대략 다음과 같은 구조였다.
location /api/ { proxy_pass http://decisionlog-backend:8080; }
문제는 Spring Boot가 자신에게 직접 들어온 요청만 기준으로 판단한다는 점이였다.

Spring Boot 입장에서는 요청이 다음과 같이 보였다.

그래서 Spring Boot는 실제 사용자가 HTTPS로 접속하고 요청을 보냈음에도 http 요청으로 받아 다른 도메인으로 판단했다.
이로 인해 다음과 같은 문제가 발생할 수 있었다.
- HTTPS 환경인데 Spring Boot가 HTTP 요청으로 인식
- OAuth Redirect URL 생성 시 http://로 생성될 가능성
- Spring Security Redirect 처리 오류 가능성
- 세션 쿠키의 Secure 처리와 실제 요청 스킴 불일치 가능성
- 프론트엔드와 백엔드 간 인증 흐름에서 예기치 않은 실패 발생 가능성
3. 원인 분석
Nginx는 SSL 인증서를 가지고 외부 HTTPS 요청을 처리한다.
HTTPS 요청
= 암호화된 HTTP 요청
Nginx가 HTTPS 요청을 복호화하면 내부에서는 일반 HTTP 요청이 된다.
그리고 Docker Network 안의 Spring Boot 컨테이너로 HTTP 요청을 전달한다.
이 구조 자체는 문제가 아니다.
오히려 운영 환경에서는 흔히 사용하는 방식이다.
외부 HTTPS 처리: Nginx
비즈니스 로직 처리: Spring Boot
문제는 Nginx가 원래 요청 정보를 헤더로 넘기더라도, Spring Boot가 그 헤더를 읽도록 설정되어 있지 않으면 외부 요청이 HTTPS였다는 사실을 모른다는 점이었다.
Nginx에서는 보통 다음 헤더를 전달한다.
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
하지만 Spring Boot에서 Forward Header 처리 전략을 설정하지 않으면, 이 정보를 제대로 반영하지 못할 수 있다.
4. 해결 방법
생각보다 간단한 방법이였는데
Spring Boot 환경 변수에 다음 설정을 추가하였다.
SERVER_FORWARD_HEADERS_STRATEGY=framework
이는 Spring Boot Property 기준으로 다음 설정과 같다.
server.forward-headers-strategy=framework
이 설정을 추가하면 Spring Boot가 X-Forwarded-* 계열 헤더를 해석한다.
즉, Spring Boot가 내부 HTTP 요청만 보는 것이 아니라, Nginx가 전달한 원래 요청 정보를 기준으로 판단한다.
적용 후 Spring Boot는 다음과 같이 인식하게 된다.
X-Forwarded-Proto: https
X-Forwarded-Host: decisionlog.duckdns.org
결과적으로 Spring Boot는 실제 사용자가 접속한 주소를 다음처럼 올바르게 판단한다.
https://decisionlog.duckdns.org
5. 선택한 해결 방안
이번 문제를 해결하는 방법은 크게 두 가지가 있었다.
첫 번째는 Spring Boot 자체에 HTTPS 인증서를 적용해서 내부 구간까지 HTTPS로 구성하는 방법이다.
Nginx → HTTPS → Spring Boot
하지만 이 방식은 다음 문제가 있다.
- Spring Boot에도 인증서 설정이 필요함
- 컨테이너별 인증서 관리가 복잡해짐
- 서비스가 늘어날수록 인증서 관리 포인트가 증가함
- Nginx를 둔 장점이 줄어듦
두 번째는 Nginx에서 HTTPS를 종료하고, 내부는 HTTP로 유지하되 원래 요청 정보를 Spring Boot에 전달하는 방법이다.
사용자 → HTTPS → Nginx → HTTP → Spring Boot
이번에는 두 번째 방식을 선택하였다.
이유는 다음과 같다.
- Nginx가 SSL 인증서를 통합 관리할 수 있음
- Spring Boot는 기존처럼 8080 HTTP 포트만 유지하면 됨
- Docker Network 내부 통신 구조가 단순함
- 실제 운영 환경에서 일반적으로 사용하는 구조임
- 문제의 본질이 암호화가 아니라 요청 스킴 인식 문제였음
또한 기존 HTTP 환경에서는 정상 동작하던 애플리케이션을 HTTPS 환경에서도 동일하게 운영하기 위해서는 내부 통신 구조를 변경하기보다, 프록시 환경에서 전달되는 요청 정보를 올바르게 해석하는 것이 더 적절한 해결 방법이라고 판단하였다.
따라서 내부 통신을 HTTPS로 바꾸기보다, Spring Boot가 Proxy Header를 올바르게 해석하도록 설정하는 것이 더 적절하다고 판단했다.
'Deployment > Server' 카테고리의 다른 글
| [Server/ssh] SSH 접속 및 private, public key 이해하기 (0) | 2025.09.10 |
|---|---|
| [Server/Linux] ubuntu 기반 VM ssh key 등록 (0) | 2025.09.10 |
| [Server/NGINX] NGINX 설치 & 설명에 대한 모든 것 (0) | 2024.12.06 |