본문으로 건너뛰기

Apache SSL/TLS 설정 완전 정복

Apache HTTPD는 mod_ssl 모듈을 통해 HTTPS를 지원합니다. SSLEngine, SSLProtocol, SSLCipherSuite 등 핵심 지시어를 설정해 안전한 HTTPS 환경을 구성합니다.


mod_ssl 설치 및 활성화

# Ubuntu/Debian
sudo apt install libapache2-mod-ssl
sudo a2enmod ssl
sudo a2enmod headers # 보안 헤더용
sudo a2enmod rewrite # HTTP→HTTPS 리다이렉트용
sudo systemctl restart apache2

# CentOS/RHEL (대부분 기본 포함)
sudo dnf install mod_ssl
sudo systemctl restart httpd

# 활성화 확인
apache2ctl -M | grep ssl
# ssl_module (shared)

기본 HTTPS VirtualHost 설정

# /etc/apache2/sites-available/example.com-ssl.conf

<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com

DocumentRoot /var/www/html
DirectoryIndex index.html index.php

# SSL 활성화
SSLEngine On

# 인증서 파일
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

# 애플리케이션 설정
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>

# 로그
CustomLog /var/log/apache2/example.com-ssl-access.log combined
ErrorLog /var/log/apache2/example.com-ssl-error.log
</VirtualHost>
# 사이트 활성화
sudo a2ensite example.com-ssl.conf
sudo systemctl reload apache2

TLS 버전과 암호 스위트 전역 설정

전역 SSL 설정은 ssl.conf 또는 apache2.conf에서 관리합니다.

# /etc/apache2/mods-available/ssl.conf 또는 conf-available/ssl-security.conf

# TLS 버전: 1.2와 1.3만 허용
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

# 암호 스위트 (TLS 1.2용)
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384

# TLS 1.3 암호 스위트
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256

# 서버 암호 스위트 우선순위 (off = 클라이언트 우선, TLS 1.3 권장)
SSLHonorCipherOrder off

# ECDH 곡선
SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1

# 세션 캐시
SSLSessionCache shmcb:/run/apache2/ssl_scache(512000)
SSLSessionCacheTimeout 300

# OCSP Stapling
SSLUseStapling On
SSLStaplingCache shmcb:/run/apache2/ssl_stapling(32768)
SSLStaplingReturnResponderErrors off

HTTP → HTTPS 리다이렉트

# /etc/apache2/sites-available/example.com.conf

<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com

# Let's Encrypt 인증용 경로는 제외
Alias /.well-known/acme-challenge/ /var/www/html/.well-known/acme-challenge/
<Directory /var/www/html/.well-known/acme-challenge/>
Require all granted
</Directory>

# 나머지 모든 요청 → HTTPS
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

또는 더 간단하게:

<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>

HSTS 및 보안 헤더 설정

<VirtualHost *:443>
ServerName example.com
SSLEngine On
# ... 인증서 설정 ...

# 보안 헤더 (mod_headers 필요)
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

# 서버 정보 숨기기
ServerTokens Prod
ServerSignature Off
</VirtualHost>

완전한 프로덕션 Apache HTTPS 설정

# /etc/apache2/sites-available/production-ssl.conf

<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html

# SSL 설정
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

# TLS 버전
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

# 암호 스위트
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off

# OCSP Stapling
SSLUseStapling On

# 보안 헤더
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'"

# 리버스 프록시 (Tomcat으로)
ProxyRequests Off
ProxyPreserveHost On
ProxyPass "/" "http://localhost:8080/"
ProxyPassReverse "/" "http://localhost:8080/"

# 백엔드에 HTTPS 정보 전달
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"

# 로그
CustomLog /var/log/apache2/ssl-access.log combined
ErrorLog /var/log/apache2/ssl-error.log
</VirtualHost>

설정 검증

# 문법 확인
sudo apachectl configtest

# 적용
sudo systemctl reload apache2

# SSL 설정 확인
openssl s_client -connect example.com:443 -tls1_3 2>/dev/null | head -20

# Apache SSL 상태 확인
sudo apachectl -D DUMP_RUN_CFG | grep SSL

# 포트 확인
sudo ss -tlnp | grep -E '80|443'

멀티 도메인 HTTPS (SNI)

하나의 서버에서 여러 도메인의 HTTPS를 운영합니다.

# 도메인 A
<VirtualHost *:443>
ServerName example.com
SSLEngine On
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
DocumentRoot /var/www/example.com
</VirtualHost>

# 도메인 B
<VirtualHost *:443>
ServerName another.com
SSLEngine On
SSLCertificateFile /etc/ssl/another.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/another.com/privkey.pem
DocumentRoot /var/www/another.com
</VirtualHost>

SNI(Server Name Indication) 덕분에 443 포트 하나로 여러 인증서를 사용할 수 있습니다. 현대 브라우저는 모두 SNI를 지원합니다.

다음 페이지에서는 HTTPS 오프로딩(SSL Termination) — 로드밸런서에서 TLS를 종료하고 HTTP로 백엔드에 전달하는 방법을 알아봅니다.