키워드 : HTTPS, SSL/TLS, Cloudflare, DNS, NS, Certbot, Let's Encrypt

지금까지 홈서버에 접속하려면
`http://211.xxx.xxx.xxx` 처럼 공인 IP를 직접 입력해야 했다.
완성도도 떨어져보일뿐더러,
무엇보다 HTTP만 쓰니 안전하지 않다.
이전 프로젝트에서 배포를 위해 `waggle-waggle.com` 이라는 도메인을 구매했었다.
홈서버를 위해 새로 살까 고민했지만.. 비용도 비용이고해서 재활용하기로 했다.
이번 글에서는 기존에 가지고 있던 도메인을 홈서버에 연결하고, HTTPS까지 적용하려고 한다.
도메인 연결을 위해서는 HTTPS가 필요한 건가? ❌
이전에는 도메인 연결을 위해 HTTPS가 필요한거고,
때문에 대부분의 실습에서 이를 함께 진행하는 것이라 생각했는데, 반대였다.
HTTPS 통신을 위해 SSL 인증서를 발급받으려면
인증기관(ex: Let's Encrypt)이 "이 서버가 진짜 도메인 소유자인지" 검증하는데,
이때 도메인 → 서버로 실제 요청을 보내며 검증한다.
즉, DNS가 먼저 홈서버를 가리키고 있어야 이 검증이 통과된다.
음,, 도메인 연결 과정 자체는 간단하지만 새로운 개념이나 헷갈리는 부분들이 생각보다 많았다.
DNS, SSL, NS 등 용어와 구조를 이해할 필요를 느꼈다.
<실습 순서>
(1) 도메인 구매 → "waggle-waggle.com 이름에 대한 소유권"
(2) DNS 설정 → "이 이름을 어떤 IP로 연결할 건지 지정"
(3) SSL 인증서 → "이 연결을 암호화"
(4) nginx 설정 → 443 열고, 80은 443으로 리다이렉트
Cloudflare를 사용한 이유
도메인을 샀다는 건, 전 세계에서 이 이름을 나만 쓸 수 있다는 소유권을 산 거다.
근데 이 이름이 "어디로 연결되는지"는 별개 문제다.
💡 DNS = 이름을 IP로 변환하는 시스템
: Domain Name System
사람은 `waggle-waggle.com` 이라고 치지만, 컴퓨터는 IP 주소로만 통신한다.
그래서 중간에 "이 도메인 이름의 IP가 뭐야?" 를 알려주는 시스템이 필요한데, 그게 DNS다.
브라우저: waggle-waggle.com 접속하고 싶어
↓
DNS 서버에 물어봄: "이 도메인의 IP가 뭐야?"
↓
DNS 서버: "211.62.65.139"
↓
그 IP로 연결
💡 NS = DNS 정보를 실제로 갖고있는 서버
: Name Server
DNS 시스템 안에서도 "그래서 이 도메인의 IP는 누가 알고있어?" 를 담당하는 서버가 필요하다.
그게 네임서버(NS)다.
기본적으로 도메인을 사면 산 곳에서 자기네 네임서버를 붙여준다.
그 네임서버가 있는 곳에서 "이 도메인은 이 IP야" 라는 정보(레코드)를 관리하게 되는 것이다.
💡 도메인 소유권(구매처)와 DNS관리(네임서버)는 분리가능
이게 핵심이다.
Route 53에서 도메인을 샀어도 네임서버를 Cloudflare로 바꾸면,
DNS 관리는 Cloudflare에서 할 수 있다.
나는 Route 53에서 도메인을 샀으니 기본적으로 AWS 네임서버가 붙어있는데,
AWS에서 DNS를 직접 관리하려면 호스팅 영역(Hosted Zone) 을 생성해야 하고,
이게 월 $0.5가 청구된다. (1년에 약 9000원..)
하지만 네임서버를 Cloudflare로 바꾸면 DNS 관리를 그쪽에서 무료로 할 수 있다.
이런 비용 차이도 있지만, Cloudflare를 선택한 이유는
실무에서 많이 쓰인다는 것이 더 크게 다가왔던 것 같다.
(무료임에도 DDoS 방어, 빠른 DNS 전파, 트래픽 분석이 기본 포함되어 있다)
Route 53 (도메인 소유권 유지)
↓ 네임서버를 Cloudflare NS로 변경
Cloudflare (DNS 관리, 무료)
↓ A 레코드: daehyoung.waggle-waggle.com → 공인IP
홈서버
Step 1. Cloudflare DNS 연결
(1) 레코드 등록
Cloudflare 대시보드에 도메인을 추가하면 DNS 레코드 관리 화면이 나온다.
여기서 레코드를 추가한다.
레코드 : DNS 서버에 저장되는 매핑 정보다. A 레코드는 "도메인 이름 → IPv4 주소" 를 매핑함.

(2) 네임서버(NS) 교체
A 레코드를 등록해도 아직 Cloudflare가 이 도메인의 NS 역할을 하고 있지 않다.
Route 53에서 NS를 Cloudflare로 교체해야 한다.
예전에 쓰던 4개의 NS를 지워주고 Cloudflare에서 발급받은 NS 2개로 교체해준다.


(3) nslookup으로 DNS 적용확인
nslookup 은 DNS 서버에 "이 도메인의 IP가 뭐야?" 하고 직접 질문하는 명령어다.
nslookup daehyoung.waggle-waggle.com


또 브라우저로 접속해도 잘 동작하는 것 확인가능함.
도메인 연결 → 완료 ✅
이제 HTTPS를 적용해보자 !
Step 2. Let's Encrypt SSL 발급
도메인은 연결했지만, 큰 문제가 있다.
로그인, 개인정보 등 민감한 데이터가 평문으로 전송되고있다는 것
실무에서도 HTTP만 쓰는 서비스는 없다.
💡 HTTP vs HTTPS
HTTP(HyperText Transfer Protocol)
: 브라우저에서 서버로 데이터를 주고받을 때 쓰는 통신 규약
하지만 HTTP는 데이터를 평문(Plain Text) 으로 전송한다.
때문에 중간에 누군가 패킷을 가로채면 내용을 그대로 읽을 수 있음.
HTTP: [브라우저] ──평문──▶ [서버]
"password=1234" 그대로 노출
HTTPS: [브라우저] ──암호화──▶ [서버]
"xK92#mPq..." 해독 불가
HTTPS 는 HTTP에 TLS(Transport Layer Security) 암호화를 얹은 거.
💡 SSL 인증서
HTTPS 암호화를 하려면 SSL 인증서가 필요하고 얘는 2가지 역할을 함
1. 신원 증명 : "이 서버가 진짜 daehyoung.waggle-waggle.com 이 맞습니까?" 를 공인된 기관(CA)이 보증해줌
2. 암호화 키 교환 : 브라우저와 서버가 암호화 통신을 시작할 때 서로 키를 교환하는 데 이 인증서가 쓰임
브라우저: 이 서버 진짜 맞냐?
↓
서버: 인증서 제출 (ㅇㅇ CA가 서명한 거임)
↓
브라우저: ㅇㅎ CA 확인 완료. 신뢰할게요. 암호화 통신 시작.
# 인증서가 없거나 신뢰할 수 없는 인증서인 경우
# → ⚠️ 연결이 비공개로 설정되어 있지 않습니다 (우리가 자주보는 것)
💡 Let's Encrypt
원래 SSL 인증서는 CA에 돈을 내고 구매해야 했음(연간 수만 원~수십만 원)
Let's Encrypt 는 2015년부터 인증서를 무료로 발급해주는 비영리 CA다.
(현재 전세계 많은 HTTPS 사이트들이 요거 씀)
유효기간은 90일이고, Certbot 이라는 도구로 발급과 자동 갱신을 처리할 수 있다.
💡 인증서 발급이 가능한 조건 with HTTP 챌린지
Let's Encrypt가 인증서를 발급할 때 한 가지를 검증한다.
"이 도메인의 진짜 소유자가 이 서버를 운영하고 있는가?"
가장 많이 쓰이는 검증 방식이 HTTP 챌린지다.
Let's Encrypt가 서버에 특정 파일을 요청해보고,
응답이 오면 "이 서버가 도메인 소유자가 맞다" 고 판단하는 것이다.
Let's Encrypt: daehyoung.waggle-waggle.com/.well-known/acme-challenge/xxxxx 에 파일 있어?
↓
서버: 있어요 (응답)
↓
Let's Encrypt: 도메인 소유 확인 완료. 인증서 발급.
그래서 Step 1을 통해 먼저 도메인을 연결한 것이었다.
DNS가 홈서버를 가리키지 않으면 Let's Encrypt가 챌린지 요청을 보낼 곳이 없기 때문.
(1) 홈서버에 Certbot 설치
sudo apt update && sudo apt install certbot python3-certbot-nginx -y
# python3-certbot-nginx : Certbot이 nginx 설정을 자동으로 읽고 수정하게 해주는 플러그인
# 이게 있어야 나중에 자동으로 nginx에 인증서 적용 가능
(2) 인증서발급
sudo certbot --nginx -d daehyoung.waggle-waggle.com
# 이메일 : 내꺼 입력 (인증서 만료 알림받는 용)
# Terms of Service : Y
# 뉴스레터 수신 : N
(3) HTTP 챌린지
⚠️ 트러블슈팅 - HTTP 챌린지 실패

nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
HTTP 챌린지를 수행하기 위해 Certbot이 호스트:80의 Nginx 서비스를 제어하려고 시도했다.
하지만 현재 아키텍처에서는 호스트:80에 직접 nginx를 띄우고 있지 않다.
지금 80번 포트를 사용하고 있는 것은 Docker 내부의 nginx 컨테이너다.
결국 80번 포트는 nginx가 아닌 Docker가 점유하고 있었기 때문에 HTTP 챌린지 검증이 실패한 것.
처음에는 Docker를 중지하거나 호스트에 별도로 Nginx를 구성해야 하나.. 고민했지만,
현재 구조에서는 HTTP 챌린지보다 DNS 챌린지를 사용하는 방식이 더 적절했다.
🚀 해결: HTTPS 챌린지가 아닌 DNS 챌린지
이 방식은 80번 포트를 전혀 안쓰기에 Docker를 내릴 필요도, 포트 충돌도 없다
Let's Encrypt: DNS에 이 TXT 레코드 추가해봐
↓
Certbot이 Cloudflare API로 자동으로 TXT 레코드 추가
↓
Let's Encrypt: DNS 확인 완료 → 인증서 발급
- DNS 챌린지를 위해서 일단 Cloudflare API 토큰을 발급받는다
- 이후 홈서버에 Cloudflare DNS 플러그인을 설치하고
- Cloudflare API 토큰을 파일로 저장한다. (Certbot이 자동으로 이 파일을 읽어서 인증)
- 다시 DNS 챌린지로 인증서 발급 진행


결과 : /etc/letsencrypt/live/daehyoung.waggle-waggle.com/fullchain.pem 에 발급된 인증서가 저장됨
Step 3. nginx HTTPS 적용
이제 발급받은 SSL 인증서를 nginx에 연결해서 HTTPS를 활성화하면 끝이다.
(현재 상태)
외부 → 443포트 → ? (아무것도 없음)
외부 → 80포트 → nginx → 앱
(목표)
외부 → 443포트 → nginx (SSL 인증서 적용) → 앱
외부 → 80포트 → nginx → 443으로 리다이렉트
(1) 인증서 볼륨 마운트
그전에, 인증서 파일이 호스트에 있고, nginx는 Docker 컨테이너 안에서 돌고있으므로
이 인증서를 찾을 수 있도록 볼륨 마운트로 연결해줘야 한다.
# docker-compse.yml의 nginx 서비스쪽
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt:ro (추가)
ports:
- "80:80"
- "443:443" (추가)
(2) nginx.conf 수정
# nginx.conf
...
# 기존 HTTP는 HTTPS로 리다이렉트되게함
server {
listen 80;
server_name daehyoung.waggle-waggle.com;
return 301 https://$host$request_uri;
# 80으로 오는 모든 요청을 443으로 301 리다이렉트
}
# HTTPS
server {
listen 443 ssl;
server_name daehyoung.waggle-waggle.com;
# 인증서
ssl_certificate /etc/letsencrypt/live/daehyoung.waggle-waggle.com/fullchain.pem;
# 개인키
ssl_certificate_key /etc/letsencrypt/live/daehyoung.waggle-waggle.com/privkey.pem;
location / {
proxy_pass http://frontend:80;
}
location /api {
proxy_pass http://green;
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 상태코드 301 (Moved Permanently)
: 요청한 리소스가 새로운 URL로 완전히 영구 이동되었음을 의미. 브라우저가 새 주소를 캐싱한다.
(3) HTTPS 적용 확인
curl -v https://daehyoung.waggle-waggle.com 을 입력 후 터미널의 응답을 보면
# TLS 핸드셰이크 과정
Client hello → "나 TLS 쓸 수 있어, 이런 암호화 방식 지원해"
Server hello → "그럼 이 방식으로 하자, 내 인증서 받아"
Certificate → 서버가 인증서 전달
CERT verify → 클라이언트가 인증서 검증
Finished → 핸드셰이크 완료, 암호화 통신 시작
# 인증서 정보
* subject: 이 인증서가 발급된 도메인
* issuer: 발급 기관(Let's Encrypt)
+ 브라우저에서 http://daehyoung.waggle-waggle.com 접속시 자동으로 https:// 로 바뀌는 것도 확인!
