HTTPS 오프로딩 (SSL Termination)
HTTPS 오프로딩(SSL Termination)은 웹서버(로드밸런서)에서 TLS를 종료하고, 내부 백엔드 서버와는 HTTP로 통신하는 아키텍처 패턴입니다. TLS 처리 부담을 애플리케이션 서버에서 분리해 성능과 관리 효율성을 높입니다.
SSL Termination vs SSL Passthrough
SSL Termination (오프로딩)
[클라이언트] ──HTTPS──▶ [Nginx/Apache] ──HTTP──▶ [App Server]
TLS 종료 여기서 평문으로 통신
장점:
- 인증서가 로드밸런서 한 곳에만 있으면 됨
- 백엔드 서버는 TLS 처리 부담 없음
- 로드밸런서에서 HTTP 헤더, URL 기반 라우팅 가능
단점:
- 로드밸런서~백엔드 구간은 암호화 없음 (내부망이면 허용)
- End-to-End 암호화 필요 시 부적합
SSL Passthrough (패스스루)
[클라이언트] ──HTTPS──▶ [L4 LB] ──HTTPS──▶ [App Server]
TLS 그대로 전달 TLS 종료 여기서
장점: End-to-End 암호화 유지 단점: L4 레벨 라우팅만 가능, 헤더 기반 라우팅 불가
Nginx SSL Termination 설정
# /etc/nginx/conf.d/ssl-termination.conf
upstream backend_http {
server app1.internal:8080;
server app2.internal:8080;
server app3.internal:8080;
keepalive 32;
}
# HTTPS 오프로딩
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
location / {
# HTTP로 백엔드 전달
proxy_pass http://backend_http;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 원래 HTTPS 정보를 헤더로 전달 (필수!)
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
# HTTP 리다이렉트
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Apache SSL Termination 설정
# 인증서는 Apache에만, 백엔드는 HTTP
<VirtualHost *:443>
ServerName example.com
SSLEngine On
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
ProxyRequests Off
ProxyPreserveHost On
# HTTP로 로드밸런서 그룹에 전달
<Proxy "balancer://backend">
BalancerMember "http://app1.internal:8080"
BalancerMember "http://app2.internal:8080"
ProxySet lbmethod=bybusyness
</Proxy>
ProxyPass "/" "balancer://backend/"
ProxyPassReverse "/" "balancer://backend/"
# HTTPS 정보 헤더로 전달
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
</VirtualHost>
백엔드 애플리케이션에서 X-Forwarded-Proto 처리
백엔드 애플리케이션은 실제 클라이언트가 HTTPS로 접속했는지 알 수 없습니다. X-Forwarded-Proto 헤더를 읽어 리다이렉트, HSTS, 쿠키 Secure 속성 등을 올바르게 처리해야 합니다.
Spring Boot 설정
// application.yml
server:
forward-headers-strategy: native # X-Forwarded-* 헤더 신뢰
# 또는 Spring Boot 2.x에서
server:
use-forward-headers: true
// Spring Security에서 HTTPS 강제 (오프로딩 환경)
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// X-Forwarded-Proto 헤더 기준으로 HTTPS 감지
.requiresChannel(channel ->
channel.requestMatchers(r ->
r.getHeader("X-Forwarded-Proto") != null
).requiresSecure()
)
// HSTS
.headers(headers -> headers
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(63072000)
)
);
return http.build();
}
}
Tomcat에서 RemoteIpValve 설정
<!-- server.xml: X-Forwarded-Proto를 신뢰하도록 설정 -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-Real-IP"
protocolHeader="X-Forwarded-Proto"
protocolHeaderHttpsValue="https"
trustedProxies="10\.0\.0\.\d{1,3}" <!-- 신뢰할 프록시 IP 범위 -->
/>
SSL Re-encryption (내부망도 암호화)
보안 요건이 엄격한 환경에서는 로드밸런서~백엔드 구간도 HTTPS를 사용합니다.
upstream backend_https {
server app1.internal:8443;
server app2.internal:8443;
}
server {
listen 443 ssl;
# ... 클라이언트측 SSL 설정 ...
location / {
proxy_pass https://backend_https; # 백엔드도 HTTPS
# 내부 인증서 검증 설정
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/internal-ca.crt;
proxy_ssl_certificate /etc/ssl/client.crt; # mTLS
proxy_ssl_certificate_key /etc/ssl/client.key;
proxy_set_header X-Forwarded-Proto https;
}
}
SSL Termination 아키텍처별 인증서 관리
| 구성 | 인증서 위치 | 내부 통신 |
|---|---|---|
| 단순 Termination | LB 1개 | HTTP (평문) |
| HA LB + Termination | LB 2대 공유 (Keepalived) | HTTP (평문) |
| Re-encryption | LB + 각 App | HTTPS (내부 인증서 가능) |
| Passthrough | 각 App | HTTPS (클라이언트 인증서) |
성능 비교
TLS 처리는 CPU를 사용합니다. Nginx가 TLS를 담당하면 Tomcat의 CPU를 절약합니다.
# TLS 오프로딩 없이 (Tomcat이 직접 처리)
ab -n 10000 -c 100 -k https://app:8443/api/test
# TLS 오프로딩 있음 (Nginx → Tomcat HTTP)
ab -n 10000 -c 100 -k https://nginx/api/test
# 일반적으로 오프로딩 환경이 Tomcat CPU 사용량을 30~50% 절감
다음 페이지에서는 HSTS, CSP 등 보안 강화 헤더를 알아봅니다.