Route 53 도메인을 사설망 서버에 연결하는 방법

이 문서는 AWS Route 53에서 관리하는 하나 이상의 도메인을 집이나 사무실의 서버에 연결하고, 리버스 프록시를 통해 여러 웹 서비스를 HTTPS로 제공하는 일반적인 절차를 설명한다.

예시는 Apache HTTP Server와 acme.sh를 사용하지만, DNS·NAT·TLS·리버스 프록시라는 전체 구조는 Nginx, Caddy, Traefik 등 다른 구성에서도 같다.

문서의 도메인과 IP 주소는 모두 예시다. 실제 환경에 맞게 아래 변수를 먼저 정한 뒤 명령과 설정에 일관되게 적용한다.

0. 환경 변수 정리

항목예시설명
대표 도메인example.com사용자가 접속할 기본 주소
추가 도메인example.net같은 서버에서 운영할 다른 도메인
서비스 서브도메인app.example.com서비스별 주소가 필요할 때 사용
공인 IPv4 주소203.0.113.10인터넷에서 보이는 공유기 또는 방화벽 주소
리버스 프록시 주소192.168.10.1080·443 요청을 받는 내부 서버
백엔드 주소192.168.10.20실제 애플리케이션 또는 NAS 주소
백엔드 포트30060애플리케이션이 수신하는 내부 포트
ACME webroot/var/www/example-com-acmeHTTP-01 검증 파일을 둘 디렉터리
인증서 경로/etc/ssl/example.com인증서와 개인 키를 설치할 디렉터리

203.0.113.0/24는 문서 예시에 사용하는 주소 대역이다. 실제 설정에는 ISP나 네트워크 관리자가 제공한 공인 주소를 사용한다.

1. 전체 연결 구조

DNS는 도메인을 공인 IP로 안내할 뿐이다. 실제 접속에는 DNS, 인터넷 회선, NAT 또는 방화벽, 리버스 프록시, 백엔드 서비스가 모두 정상이어야 한다.


flowchart TB
    U["사용자 브라우저<br/>https://example.com"]
    DNS["Route 53<br/>A 레코드 → 203.0.113.10"]
    GW["공유기 또는 방화벽<br/>80·443 포트 전달"]
    RP["192.168.10.10<br/>TLS 종료 및 리버스 프록시"]
    APP1["192.168.10.20:30060<br/>서비스 A"]
    APP2["192.168.10.20:30061<br/>서비스 B"]

    U -->|① 이름을 IP로 조회| DNS
    U -->|② 공인 IP의 443에 연결| GW
    GW -->|③ 내부 프록시로 전달| RP
    RP -->|④ Host 이름에 따라 분기| APP1
    RP -->|④ Host 이름에 따라 분기| APP2


구간대표 증상
DNSNXDOMAIN, 조회 결과 없음, 잘못된 IP 반환
공인 회선·포트 전달연결 시간 초과
리버스 프록시연결 거부, TLS 오류, 기본 사이트 표시
백엔드 연결502 Bad Gateway, 503 Service Unavailable
애플리케이션 설정화면은 열리지만 로그인·CORS·리다이렉트 실패

문제가 생기면 위에서 아래 순서로 확인한다.

2. 사전 확인

설정 전에 다음 조건을 확인한다.

공유기의 WAN 주소가 사설 주소 대역이거나 외부 공인 IP와 다르면 이중 NAT 또는 CGNAT일 수 있다. 이 경우 상위 장비에도 포트 전달을 설정하거나, ISP에 공인 IP를 요청하거나, 터널·VPN 기반 공개 방식을 사용해야 한다.

IPv6용 AAAA 레코드가 이미 있다면 IPv6 경로도 함께 구성해야 한다. 구성하지 않은 AAAA 레코드가 남아 있으면 일부 사용자는 정상 IPv4 경로 대신 실패하는 IPv6 경로로 접속할 수 있다.

3. Route 53 DNS 설정

3.1 호스팅 영역과 네임서버 위임

Route 53에 퍼블릭 호스팅 영역을 만들면 네임서버(NS) 네 개가 배정된다. 도메인 등록기관의 네임서버를 이 값으로 변경해야 Route 53의 레코드가 인터넷에 공개된다.

# 인터넷에서 실제로 확인되는 네임서버
dig +short NS example.com @8.8.8.8

# AWS CLI로 Route 53 호스팅 영역의 네임서버 확인
aws route53 get-hosted-zone \
  --id <HOSTED_ZONE_ID> \
  --query 'DelegationSet.NameServers'

같은 도메인의 호스팅 영역을 여러 개 만들었다면 각 영역의 NS 세트가 다를 수 있다. 등록기관에서 위임한 영역과 실제로 레코드를 수정하는 영역이 같은지 확인한다.

기존 도메인의 서브도메인만 추가할 때는 별도 호스팅 영역이나 NS 위임이 보통 필요 없다. 기존 호스팅 영역에 레코드를 추가하면 된다. 서브도메인을 별도 호스팅 영역으로 분리하려면 상위 영역에 해당 서브도메인의 NS 위임을 추가해야 한다.

3.2 레코드 구성

정점 도메인과 www를 함께 운영하는 일반적인 예시는 다음과 같다.

이름타입TTL
example.comA203.0.113.10300
www.example.comCNAMEexample.com300
app.example.comA 또는 CNAME공인 IP 또는 대표 도메인300

두 개 이상의 독립 도메인을 같은 서버에서 운영할 때는 각 호스팅 영역에 같은 공인 IP를 가리키는 A 레코드를 만든다. 공유기 포트 전달은 공인 IP와 내부 프록시가 같다면 한 번만 설정하며, 서비스 구분은 리버스 프록시가 요청의 호스트 이름으로 처리한다.

3.3 DNS 검증

dig +short example.com @8.8.8.8
dig +short example.com @1.1.1.1
dig +short www.example.com @8.8.8.8
dig +short example.net @8.8.8.8

두 곳 이상의 외부 리졸버에서 기대한 공인 IP가 나오는지 확인한다. ping은 DNS 확인 수단으로 적합하지 않다. DNS가 정상이더라도 ICMP를 차단하는 회선이나 장비가 많다.

4. 공유기와 방화벽 설정

공인 IP로 들어온 요청을 리버스 프록시 서버에 전달한다.

외부 포트내부 목적지용도
80/TCP192.168.10.10:80HTTP-01 검증 및 HTTPS 리다이렉트
443/TCP192.168.10.10:443HTTPS 서비스

외부 연결은 휴대전화 데이터나 다른 인터넷 회선에서 검사한다. 일부 공유기는 헤어핀 NAT를 지원하지 않으므로 같은 LAN에서 공인 주소로 테스트하면 실제 외부 결과와 다를 수 있다.

curl --connect-timeout 5 -I http://203.0.113.10/

5. Apache 리버스 프록시 설정

설정 파일 위치, 실행 사용자, 로그 경로, 인증서 경로는 설치 방식과 운영체제에 따라 다르다. 먼저 현재 Apache가 실제로 읽는 설정을 확인한다.

apachectl -V
apachectl -M

필요한 주요 모듈은 ssl, proxy, proxy_http, headers, rewrite다. HTTP/2를 사용할 경우 http2 모듈도 확인한다.

변경 전에는 설정 파일을 백업한다.

cp -a /path/to/httpd-vhosts.conf \
  /path/to/httpd-vhosts.conf.bak.$(date +%Y%m%d-%H%M%S)

5.1 먼저 HTTP 가상호스트 추가

인증서가 아직 없다면 :80 가상호스트부터 적용한다. 존재하지 않는 인증서 경로가 포함된 :443 설정을 먼저 추가하면 Apache 전체 설정 검사가 실패할 수 있다.

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

    Alias /.well-known/acme-challenge/ "/var/www/example-com-acme/.well-known/acme-challenge/"
    <Directory "/var/www/example-com-acme/.well-known/acme-challenge">
        Options None
        AllowOverride None
        Require all granted
    </Directory>

    RewriteEngine On
    RewriteRule ^/\.well-known/acme-challenge/ - [L]
    RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]

    ErrorLog  /var/log/httpd/example.com-error.log
    CustomLog /var/log/httpd/example.com-access.log combined
</VirtualHost>

www를 대표 주소로 통합하지 않을 경우 리다이렉트 목적지를 https://%{HTTP_HOST}%{REQUEST_URI}로 바꿀 수 있다. 단, 허용하지 않은 Host 헤더가 리다이렉트에 반영되지 않도록 가상호스트와 호스트 검증 정책을 명확히 구성한다.

적용 전에 문법을 검사하고, 성공한 경우에만 설정을 다시 읽힌다.

apachectl configtest
apachectl graceful

5.2 인증서 발급 후 HTTPS 가상호스트 추가

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

    SSLEngine on
    SSLCertificateFile    /etc/ssl/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem

    Protocols h2 http/1.1
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
    RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]

    ProxyPreserveHost On
    ProxyRequests Off
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    ProxyPass        / http://192.168.10.20:30060/
    ProxyPassReverse / http://192.168.10.20:30060/

    ErrorLog  /var/log/httpd/example.com-ssl-error.log
    CustomLog /var/log/httpd/example.com-ssl-access.log combined
</VirtualHost>

Apache는 프록시 요청에 X-Forwarded-For를 추가한다. 애플리케이션은 신뢰할 수 있는 리버스 프록시에서 온 헤더만 사용하도록 설정한다. 모든 발신자를 신뢰하도록 구성하면 클라이언트가 전달 헤더를 위조할 수 있다.

WebSocket을 사용하는 애플리케이션은 Apache 버전과 구성에 맞는 WebSocket 프록시 설정이 추가로 필요할 수 있다. 긴 요청이나 대용량 업로드가 있다면 시간 제한과 본문 크기 제한도 서비스 특성에 맞게 조정한다.

5.3 여러 도메인과 서비스 분리

각 도메인 또는 서브도메인마다 별도의 가상호스트를 만들고 다른 백엔드로 전달한다.

공개 주소내부 목적지 예시
example.com192.168.10.20:30060
example.net192.168.10.20:30061
app.example.com192.168.10.30:8080

모든 서비스는 같은 외부 443 포트를 사용할 수 있다. Apache가 TLS SNI와 Host 헤더를 기준으로 올바른 인증서와 백엔드를 선택하므로 서비스마다 공유기 포트를 추가로 열 필요가 없다.

6. TLS 인증서 발급과 자동 갱신

6.1 HTTP-01 방식

HTTP-01을 사용하려면 도메인의 A 레코드가 현재 공인 IP를 가리키고 외부 80/TCP가 리버스 프록시까지 도달해야 한다. 발급 전에 챌린지 경로를 검사한다.

mkdir -p /var/www/example-com-acme/.well-known/acme-challenge
printf 'ok\n' > /var/www/example-com-acme/.well-known/acme-challenge/ping

curl -sS \
  -H 'Host: example.com' \
  http://127.0.0.1/.well-known/acme-challenge/ping

rm -f /var/www/example-com-acme/.well-known/acme-challenge/ping
mkdir -p /etc/ssl/example.com

파일 소유권과 권한은 실제 Apache 실행 사용자에 맞춘다. 그다음 인증서를 발급하고 운영 경로에 설치한다.

/path/to/acme.sh --issue \
  -d example.com \
  -d www.example.com \
  -w /var/www/example-com-acme \
  --keylength ec-256 \
  --server letsencrypt

/path/to/acme.sh --install-cert \
  -d example.com \
  --ecc \
  --key-file /etc/ssl/example.com/privkey.pem \
  --fullchain-file /etc/ssl/example.com/fullchain.pem \
  --reloadcmd "apachectl configtest && apachectl graceful"

EC 인증서로 발급했다면 설치와 수동 갱신에도 --ecc를 사용한다. --reloadcmd를 설정해야 갱신 후 Apache가 새 인증서를 읽는다.

/path/to/acme.sh --list
/path/to/acme.sh --renew -d example.com --ecc --force

강제 갱신 시험은 인증기관의 제한에 영향을 줄 수 있으므로 반복 실행하지 않는다. 가능하면 ACME 클라이언트나 인증기관이 제공하는 시험 환경을 먼저 사용한다.

6.2 DNS-01 방식

외부 80 포트를 사용할 수 없거나 와일드카드 인증서가 필요하면 Route 53 DNS-01 방식을 사용할 수 있다.

export AWS_ACCESS_KEY_ID='<ACCESS_KEY>'
export AWS_SECRET_ACCESS_KEY='<SECRET_KEY>'

/path/to/acme.sh --issue \
  --dns dns_aws \
  -d example.com \
  -d '*.example.com' \
  --keylength ec-256

AWS 자격 증명은 문서, 셸 이력, 저장소, 컨테이너 이미지에 남기지 않는다. 전용 IAM 주체를 만들고 ACME에 필요한 Route 53 작업만 최소 권한으로 허용한다. 가능한 경우 짧은 수명의 자격 증명 또는 안전한 비밀 저장소를 사용한다.

와일드카드 인증서 *.example.comapp.example.com 같은 1단계 서브도메인을 포함하지만 정점 example.com이나 a.b.example.com을 자동으로 포함하지 않는다. 필요한 이름은 인증서 요청에 명시한다.

DNS-01을 사용하더라도 사용자가 http://로 접속했을 때 HTTPS로 안내하려면 80 포트를 열어 둘 수 있다. HTTP 접근을 전혀 제공하지 않을 정책이라면 80 포트는 필수가 아니다.

7. 애플리케이션 설정

리버스 프록시 설정만으로 애플리케이션의 공개 주소가 자동 변경되지는 않는다. 프레임워크와 서비스에 따라 다음 값을 실제 HTTPS 주소로 맞춘다.

설정 종류예시잘못되었을 때의 증상
공개 웹 Originhttps://example.com절대 URL이나 리다이렉트가 내부 주소를 사용
CORS 허용 Originhttps://example.com브라우저의 API 요청 거부
OAuth 기본 URLhttps://example.com로그인 후 잘못된 주소로 이동
허용 리다이렉트 목록https://example.comredirect_uri 검증 실패
Secure Cookie 설정HTTPS 및 프록시 인식로그인 세션이 유지되지 않음
Trusted Proxy리버스 프록시 주소만IP·스킴 인식 오류 또는 헤더 위조 위험

환경 변수 파일을 변경한 뒤에는 애플리케이션의 배포 방식에 맞게 컨테이너나 서비스를 재생성한다. 단순 재시작만으로 새 환경 변수가 반영되지 않는 플랫폼도 있다.

백엔드 포트는 가능하면 사설 주소에만 바인딩한다. 예를 들어 컨테이너 포트를 게시할 때 192.168.10.20:30060:30060처럼 내부 인터페이스를 지정하면 다른 인터페이스를 통한 불필요한 노출을 줄일 수 있다. 실제 방화벽 정책도 함께 확인한다.

8. OAuth와 외부 서비스 콘솔

소셜 로그인, 결제, 웹훅 등을 사용한다면 각 공급자 콘솔에도 새 도메인을 등록한다.

일반적인 OAuth 콜백 형태는 다음과 같다.

https://example.com/api/auth/callback/<provider>

공급자마다 다음 항목의 이름과 요구 조건이 다를 수 있다.

정점 도메인과 www를 모두 서비스하더라도 OAuth 기준 주소는 하나로 통일하는 편이 쿠키, 세션, 콜백 검증 문제를 줄인다.

9. 전 구간 검증

각 도메인에 대해 다음 검사를 순서대로 실행한다.

# ① DNS
dig +short example.com @8.8.8.8

# ② 80 → HTTPS 리다이렉트
curl -sS -o /dev/null \
  -w '%{http_code} -> %{redirect_url}\n' \
  http://example.com/

# ③ TLS 인증서의 이름과 유효기간
openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# ④ 실제 HTTPS 응답
curl -sS -o /dev/null \
  -w '%{http_code}\n' \
  https://example.com/

# ⑤ LAN에서 백엔드 직접 확인
curl -sS -o /dev/null \
  -w '%{http_code}\n' \
  http://192.168.10.20:30060/

외부 경로는 반드시 다른 회선에서도 확인한다. 브라우저가 이전 301 리다이렉트나 HSTS 정책을 캐시했을 수 있으므로 시크릿 창이나 새 프로필도 활용한다.

두 도메인을 운영한다면 새 설정뿐 아니라 기존 도메인도 함께 검사한다. 가상호스트가 같은 설정 파일에 있으면 한 서비스의 문법 오류나 기본 가상호스트 순서 변경이 다른 서비스에도 영향을 줄 수 있다.

10. 새 도메인 또는 서비스 추가 체크리스트

  1. 사용할 도메인, 공개 URL, 내부 백엔드 주소와 포트를 정한다.
  2. 새 독립 도메인이면 등록기관의 NS를 Route 53 호스팅 영역으로 위임한다.
  3. 기존 도메인의 서브도메인이면 기존 호스팅 영역에 A 또는 CNAME 레코드를 추가한다.
  4. DNS가 외부 리졸버에서 올바른 공인 IP로 조회되는지 확인한다.
  5. 최초 구성이라면 공유기에서 80·443을 리버스 프록시로 전달한다. 기존 프록시를 함께 쓰는 경우 보통 추가 포트 전달은 필요 없다.
  6. 백엔드 서비스를 시작하고 LAN에서 직접 응답을 확인한다.
  7. 리버스 프록시 설정을 백업한다.
  8. HTTP 가상호스트와 ACME webroot를 추가한다.
  9. 설정 검사를 통과시킨 뒤 HTTP 설정을 적용하고 챌린지 URL을 확인한다.
  10. 인증서를 발급하고 자동 갱신 후 재적재 명령을 설정한다.
  11. HTTPS 가상호스트와 백엔드 프록시 설정을 추가한다.
  12. 애플리케이션의 공개 URL, CORS, 쿠키, trusted proxy 설정을 바꾸고 재배포한다.
  13. OAuth, 웹훅 등 외부 공급자 콘솔의 URL을 변경한다.
  14. DNS부터 백엔드까지 전 구간을 외부 회선에서 검사한다.
  15. 함께 운영되는 기존 도메인과 인증서 자동 갱신도 회귀 검사한다.

11. 자주 발생하는 문제

증상가능한 원인과 확인 사항
Route 53에 레코드를 넣었지만 조회되지 않음등록기관의 NS 위임 누락, 잘못된 호스팅 영역 수정, DNS 전파 또는 캐시
일부 사용자만 접속 실패잘못된 AAAA 레코드, IPv6 방화벽·라우팅 미구성, 지역별 DNS 캐시
외부 연결이 시간 초과됨ISP 포트 차단, CGNAT, 이중 NAT, 공유기 또는 서버 방화벽, 잘못된 포트 전달
외부에서는 되지만 내부에서만 실패헤어핀 NAT 미지원. 내부 DNS에서 사설 IP를 반환하는 split-horizon DNS 검토
갑자기 도메인이 접속되지 않음유동 공인 IP 변경. 현재 공인 IP와 A 레코드를 대조하고 DDNS 검토
인증서 발급 실패DNS가 다른 IP를 가리킴, 80 포트 미도달, 챌린지 경로까지 리다이렉트·차단
인증서 설치 대상을 찾지 못함EC 발급 후 --ecc 누락 또는 인증서의 주 도메인 불일치
인증서 파일은 갱신됐지만 서비스 인증서는 만료됨갱신 후 Apache 재적재 명령 누락 또는 재적재 실패
새 가상호스트 추가 후 전체 재적재 실패문법 오류, 존재하지 않는 인증서 파일, 모듈 누락
예상과 다른 사이트가 표시됨ServerName·ServerAlias 오류, 요청 Host 불일치, 기본 가상호스트 순서
502 Bad Gateway백엔드 중지, 주소·포트 오류, 내부 방화벽, 컨테이너 포트 미게시
화면은 열리지만 로그인 실패공개 URL·CORS·쿠키·trusted proxy 설정 또는 공급자 콜백 URL 불일치
환경 변수 변경이 반영되지 않음서비스가 환경을 시작 시에만 읽음. 컨테이너 또는 서비스를 재생성해야 함
변경 후에도 브라우저가 이전 주소로 이동301 또는 HSTS 캐시. 시크릿 창과 명령줄 요청으로 분리 확인

12. 운영 권장 사항