집에서 WSL로 HTTPS(HTTP/2) 서버 운영하기: 단계별 가이드
"Learning HTTP/2" 의 실습을 위해 세팅하는 과정이지만, 평소에 홈서버를 운영하고 싶던 분들에게도 큰 도움이 될 것이라 생각합니다. 실제 집에서 자주 구축되어있는 네트워크와 랩탑 환경에서 어떻게 서버를 운영할 수 있을지 상세하게 적었습니다.
[들어가며]
개인 프로젝트나 학습 목적으로 집에서 웹 서버를 운영하고 싶을 때가 있습니다. 특히 리눅스 환경이 익숙하다면 Windows의 WSL(Windows Subsystem for Linux)을 활용하는 것이 매력적인 선택지입니다. 하지만 가정집 네트워크 환경은 보통 공유기(NAT) 뒤에 있고, WSL은 또다시 Windows 내의 가상 네트워크에 위치하는 다소 복잡한 구조입니다. 이 글에서는 이러한 다단계 네트워크 환경에서 외부 인터넷 사용자가 여러분의 WSL 서버에 HTTPS(HTTP/2)로 안전하게 접속할 수 있도록 설정하는 전체 과정을 단계별로 안내합니다. Let's Encrypt를 이용한 무료 TLS 인증서 발급 및 적용, 그리고 HTTP/2 서버 구동까지 포함합니다.
(현재 시중의 주요 브라우저들은 보안(암호화)되지 않은 HTTP/2를 지원하지 않으므로 사실상 TLS 위에서만 HTTP/2를 사용하는 것이 일반적입니다. 따라서 아래 실습을 위해선 도메인을 획득하는 것을 권장드립니다. namecheap 등에서 저렴한 도메인을 사면 1년에 $1-2 정도로 구매가 가능합니다.)
개요: 전체 흐름 이해하기
우리가 구축할 환경은 다음과 같은 데이터 흐름을 가집니다.
[인터넷 사용자] --- HTTPS 요청 (도메인:443) ---> [우리 집 공인 IP]
|
V
[1. ipTIME 공유기] <--- 포트 포워딩 (외부 443 -> 내부 Windows IP:443)
| (외부 80 -> 내부 Windows IP:80, 인증서 발급/갱신용)
V
[2. Windows PC] <--- 방화벽 허용 & 포트 프록시 (Windows IP:443 -> WSL IP:443)
| (Windows IP:80 -> WSL IP:80)
V
[3. WSL (리눅스)] <--- HTTPS 서버 (nghttpd) 실행 & Let's Encrypt 인증서 관리
핵심은 각 네트워크 경계(공유기, Windows)에서 들어오는 요청을 다음 단계(Windows PC, WSL)로 정확히 전달(포워딩)해주는 것입니다.
1. ipTIME 공유기 설정: 외부 요청을 Windows PC로 연결하기
가장 바깥 관문인 공유기 설정을 먼저 진행합니다. 외부 인터넷에서 우리 집 공인 IP의 특정 포트(HTTP:80, HTTPS:443)로 들어오는 요청을 내부 네트워크의 Windows PC로 전달하도록 설정해야 합니다.
1.1. 원격 관리 포트 충돌 방지 (중요!)
ipTIME 공유기는 기본적으로 80번 포트를 사용하여 웹 기반 관리자 페이지를 제공하는 경우가 많습니다. 우리가 웹 서버를 위해 80번 포트를 사용해야 하므로, 이 충돌을 피해야 합니다.
- 웹 브라우저에서 공유기 관리 페이지(보통 192.168.0.1)에 접속하여 로그인합니다.
- 고급 설정 → 시스템 관리 → 관리도구 설정 메뉴로 이동합니다.
- 원격 관리 포트 기능을 사용하지 않음으로 설정하거나, 사용해야 한다면 포트 번호를 8081 등 80 및 443이 아닌 다른 번호로 변경합니다.
- 설정을 저장하고, 확실한 적용을 위해 공유기를 재부팅하는 것이 좋습니다. (안해도 잘되긴 합니다.)
1.2. 포트 포워딩 설정: 80/443 포트를 Windows PC로
이제 외부에서 들어오는 80번(HTTP) 및 443번(HTTPS) 포트 요청을 실제 서버가 동작할 환경의 첫 관문인 Windows PC로 전달하도록 설정합니다.
- 고급 설정 → NAT/라우터 관리 → 포트포워드 설정 메뉴로 이동합니다.
- 아래와 같이 두 개의 규칙을 추가합니다.
- 내부 IP 주소: Windows PC가 공유기로부터 할당받은 사설 IP 주소를 입력해야 합니다. Windows 명령 프롬프트(cmd)나 PowerShell에서 ipconfig 명령어로 'IPv4 주소' 항목을 확인하세요. (예시: 192.168.0.2)
- DHCP 고정 할당 (권장): Windows PC의 IP 주소가 재부팅 시 변경되면 포트 포워딩이 실패합니다. 공유기의 고급 설정 → 네트워크 관리 → DHCP 서버 설정 메뉴에서 현재 Windows PC의 MAC 주소에 대해 고정 IP 주소를 할당해두면 편리합니다.
-
규칙 이름 (예시) 외부 포트 내부 IP 주소 내부 포트 프로토콜 HTTP_Server 80 192.168.0.2 80 TCP HTTPS_Server 443 192.168.0.2 443 TCP
[Q&A]
- Q: "왜 80번 포트도 포워딩해야 하나요? HTTPS(443)만 쓸 건데요."
- A: Let's Encrypt 인증서를 발급받거나 갱신할 때, 도메인 소유권을 확인하는 방법 중 하나인 HTTP-01 challenge는 80번 포트를 사용합니다. certbot --standalone 모드도 내부적으로 80번 포트를 사용하므로, 인증서 관리를 위해 80번 포트 포워딩이 필요합니다. (물론 다른 DNS의 txt레코드를 사용하는 challenge 방식도 있습니다. 그건 열 필요 없어요!)
2. Windows 설정: 공유기 요청을 WSL로 넘겨주기
이제 공유기를 통과한 요청이 Windows PC까지 도달했습니다. 다음 단계는 이 요청을 최종 목적지인 WSL(리눅스) 환경으로 전달하는 것입니다. Windows 방화벽 설정과 netsh portproxy 명령어를 사용합니다.
2.1. Windows 방화벽 인바운드 규칙 추가
Windows 자체 방화벽이 외부에서 들어오는 80번, 443번 포트 트래픽을 차단할 수 있으므로, 허용 규칙을 추가해야 합니다.
- Windows Defender 방화벽 → 고급 설정 → 인바운드 규칙 → 새 규칙을 선택합니다.
- '포트' 유형을 선택하고 '다음'을 누릅니다.
- 'TCP' 프로토콜을 선택하고, '특정 로컬 포트'에 80, 443을 입력하고 '다음'을 누릅니다.
- '연결 허용'을 선택하고 '다음'을 누릅니다.
- 규칙이 적용될 프로필(도메인, 개인, 공용)을 선택하고(보통 모두 선택) '다음'을 누릅니다.
- 규칙 이름(예: Allow HTTP/HTTPS Inbound)을 지정하고 '마침'을 누릅니다.
- PowerShell(관리자 권한) 사용: 아래 명령어로도 동일한 설정이 가능합니다.
-
netsh advfirewall firewall add rule name="Allow HTTP 80 Inbound" dir=in action=allow protocol=TCP localport=80 netsh advfirewall firewall add rule name="Allow HTTPS 443 Inbound" dir=in action=allow protocol=TCP localport=443
2.2. netsh portproxy를 이용한 WSL 포트 포워딩
WSL2는 기본적으로 Windows 호스트와는 별도의 가상 네트워크(보통 172.x.x.x 대역)를 사용합니다. 따라서 Windows가 받은 80/443 포트 트래픽을 WSL의 IP 주소와 해당 포트로 전달해주는 설정이 필요합니다. Windows의 netsh interface portproxy 명령어를 사용합니다.
- WSL의 IP 주소 확인: WSL 터미널을 열고 아래 명령어를 실행하여 WSL의 가상 이더넷 인터페이스(eth0) IP 주소를 확인합니다.(예시 출력: 172.30.90.215) 이 IP 주소는 WSL을 재시작하거나 Windows를 재부팅하면 변경될 수 있습니다.
-
ip addr show eth0 | grep "inet\s" | awk '{print $2}' | cut -d/ -f1
- netsh portproxy 설정: 관리자 권한으로 PowerShell을 열고 아래 명령어를 실행하여 포트 프록시 규칙을 설정합니다. connectaddress에는 위에서 확인한 WSL의 IP 주소를 입력합니다.
- 주의: WSL IP 변경 문제! WSL IP 주소는 고정되지 않고 변경될 수 있습니다. IP가 변경될 때마다 위 명령어를 다시 실행해야 합니다. 이를 자동화하는 스크립트를 사용하는 것이 매우 편리합니다.
-
# (선택) 기존 규칙이 있다면 삭제 (충돌 방지) netsh interface portproxy delete v4tov4 listenport=80 listenaddress=0.0.0.0 netsh interface portproxy delete v4tov4 listenport=443 listenaddress=0.0.0.0 # 새 규칙 등록 (listenaddress=0.0.0.0 은 모든 Windows IP에서 수신한다는 의미) # 예시: WSL IP가 172.30.90.215 인 경우 netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=80 connectaddress=172.30.90.215 connectport=80 netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=443 connectaddress=172.30.90.215 connectport=443 # 설정 확인 netsh interface portproxy show v4tov4
2.2.1. (권장) WSL IP 자동 업데이트 PowerShell 스크립트
아래 스크립트를 Set-PortProxyWSL.ps1 같은 이름으로 저장해두고, Windows 부팅 시 또는 WSL IP가 변경되었을 때 관리자 권한으로 실행하면 자동으로 최신 WSL IP를 가져와 netsh portproxy 규칙을 업데이트해 줍니다.
# Set-PortProxyWSL.ps1
Write-Host "Updating netsh portproxy rules for WSL..."
# 1. 기존 규칙 모두 삭제 (깔끔하게 재설정)
netsh interface portproxy reset
# 2. WSL 현재 IP 확인 (첫 번째 IP 주소만 사용)
# wsl hostname -I 명령어는 WSL 내에서 실행되어야 하므로, wsl.exe 를 통해 실행
$wslIpOutput = wsl -- bash -c "ip addr show eth0 | grep 'inet\s' | awk '{print \$2}' | cut -d/ -f1"
$wslIp = $wslIpOutput.Trim()
if (-not $wslIp) {
Write-Error "Failed to get WSL IP address. Make sure WSL is running."
exit 1
}
# 3. 새 규칙 등록
Write-Host "Setting portproxy to WSL IP: $wslIp"
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=80 connectaddress=$wslIp connectport=80
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=443 connectaddress=$wslIp connectport=443
Write-Host "Portproxy rules updated successfully."
# 설정 확인 (선택 사항)
# netsh interface portproxy show v4tov4
[Q&A]
- Q: "WSL1을 사용해도 되나요? WSL1도 IP가 다른가요?"
- A: WSL1은 Windows와 네트워크 네임스페이스를 공유하므로 별도의 IP 주소를 가지지 않고 localhost로 접근 가능합니다. 따라서 WSL1 환경에서는 netsh portproxy 설정이 필요 없습니다. 하지만 WSL2가 성능, 호환성 등 여러 면에서 우수하므로 WSL2 사용을 권장합니다. 이 가이드는 WSL2 기준입니다.
- Q: "Windows 방화벽 대신 다른 보안 프로그램을 사용하는데, 그래도 되나요?"
- A: 네, 다른 방화벽/보안 프로그램을 사용한다면 해당 프로그램에서 동일하게 TCP 80번과 443번 포트에 대한 인바운드 연결을 허용하도록 설정해야 합니다. 설정 방법은 프로그램마다 다릅니다.
3. WSL(리눅스) 설정: 인증서 발급 및 HTTPS 서버 실행
드디어 최종 목적지인 WSL 환경입니다. 여기서는 Let's Encrypt를 이용해 TLS 인증서를 발급받고, 이를 이용하여 HTTPS(HTTP/2) 서버를 구동합니다. Ubuntu/Debian 계열 리눅스를 기준으로 설명합니다.
3.1. Certbot 설치
Let's Encrypt 인증서를 쉽게 발급받고 관리해주는 도구인 Certbot을 설치합니다.
# 패키지 목록 업데이트
sudo apt update
# Certbot 설치 (apt 방식)
sudo apt install certbot -y
# 또는 Snap 방식 (최신 버전을 원할 경우)
# sudo apt install snapd
# sudo snap install core; sudo snap refresh core
# sudo snap install --classic certbot
# sudo ln -s /snap/bin/certbot /usr/bin/certbot
standalone 방식은 Certbot이 직접 80번 포트(또는 TLS-ALPN-01 challenge 사용 시 443번)를 잠시 사용하여 Let's Encrypt 서버와 통신하며 도메인 소유권을 인증받는 방식입니다. 따라서 인증서를 발급받거나 갱신하는 시점에는 해당 포트를 사용하는 다른 웹 서버(Nginx, Apache, nghttpd 등)가 실행 중이지 않아야 합니다.
# <your.domain.com> 부분은 실제 사용하는 도메인으로 변경
# 예: sudo certbot certonly --standalone -d example.com
# www.example.com 도 함께 사용한다면 -d 옵션을 여러 번 사용:
# sudo certbot certonly --standalone -d example.com -d www.example.com
sudo certbot certonly --standalone -d <your.domain.com>
- 명령어를 실행하면 이메일 주소를 입력하고 Let's Encrypt 서비스 약관에 동의하는 과정을 거칩니다.
- 성공적으로 완료되면 인증서 파일 경로가 출력됩니다.
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/<your.domain.com>/fullchain.pem
Key is saved at: /etc/letsencrypt/live/<your.domain.com>/privkey.pem
This certificate expires in 90 days. (...)
3.2.1. 인증서 파일 경로 (중요!)
Let's Encrypt 인증서 파일은 보통 다음 경로에 생성됩니다. 웹 서버 설정 시 이 경로를 사용합니다.
- 인증서 (+ 체인): /etc/letsencrypt/live/<your.domain.com>/fullchain.pem
- 개인 키: /etc/letsencrypt/live/<your.domain.com>/privkey.pem
3.3. HTTPS 서버 실행 (nghttpd 예시)
이제 발급받은 인증서를 사용하여 HTTPS(HTTP/2) 서버를 실행합니다. 여기서는 HTTP/2 구현체인 nghttp2 프로젝트에서 제공하는 간단한 웹 서버 nghttpd를 예시로 사용합니다. (실제 서비스에는 Nginx, Apache 등을 더 많이 사용합니다.)
- nghttp2 설치:
-
sudo apt install nghttp2-server -y
- 웹 콘텐츠 준비: 서버가 제공할 파일(예: index.html)을 특정 디렉토리(예: /var/www/html)에 준비합니다.
-
# 예시: 간단한 index.html 생성 sudo mkdir -p /var/www/html echo "hello, world" | sudo tee /var/www/html/index.html
- nghttpd 실행:
- -v: Verbose 모드 (로그 출력)
- 443: HTTPS 서비스 포트 (1024 미만 포트이므로 sudo 필요)
- 첫 번째 인자: 개인 키 파일 경로 (privkey.pem)
- 두 번째 인자: 인증서 파일 경로 (fullchain.pem 권장)
- -d: 웹 루트 디렉토리 경로
-
# <your.domain.com> 과 /var/www/html 경로는 실제 환경에 맞게 수정 sudo nghttpd -v 443 \ /etc/letsencrypt/live/<your.domain.com>/privkey.pem \ /etc/letsencrypt/live/<your.domain.com>/fullchain.pem \ -d /var/www/html
3.3.1. 접속 테스트
이제 모든 설정이 완료되었습니다! 외부 네트워크(예: 스마트폰 LTE)에 연결된 기기의 웹 브라우저에서 https://<your.domain.com>으로 접속해 보세요.
- 정상적으로 페이지가 보이고,
- 브라우저 주소창에 자물쇠 아이콘이 표시되며,
- 인증서 정보를 확인했을 때 Let's Encrypt에서 발급한 유효한 인증서로 나온다면 성공입니다!
- 브라우저 개발자 도구(F12)의 네트워크 탭에서 프로토콜이 h2 (HTTP/2)로 표시되는지도 확인해보세요.
- 브라우저 개발도구의 네트워크 탭에서 프로토콜 보려면 별도의 설정이 필요합니다.
[Q&A]
- Q: "Certbot의 standalone 말고 다른 인증 방식은 없나요?"
- A: 네, webroot 방식이 있습니다. 이 방식은 이미 실행 중인 웹 서버의 특정 경로(webroot)에 Certbot이 인증용 파일을 잠시 생성하고, Let's Encrypt 서버가 해당 파일에 HTTP(80번 포트)로 접근하여 소유권을 확인합니다. 웹 서버를 중지할 필요가 없다는 장점이 있지만, 웹 서버 설정 변경이 필요할 수 있습니다. standalone은 간단하게 시작하기 좋은 방식입니다.
- Q: "nghttpd 말고 Nginx나 Apache를 사용해도 되나요?"
- A: 물론입니다! Nginx나 Apache는 훨씬 강력하고 기능이 많은 웹 서버이며, 실제 서비스 운영에 더 적합합니다. 설정 방법은 각 웹 서버 문서를 참조해야 하지만, 기본적인 원리(인증서 파일 경로 지정, HTTPS 및 HTTP/2 활성화 등)는 유사합니다.
4. 인증서 자동 갱신 관리
Let's Encrypt 인증서는 유효 기간이 90일로 짧습니다. 따라서 주기적으로 갱신해야 합니다. Certbot은 설치 시 대부분 자동으로 갱신을 위한 스케줄러(Systemd Timer 또는 Cron Job)를 설정합니다.
- 갱신 테스트: 아래 명령어로 갱신 과정이 정상적으로 동작하는지 미리 테스트해볼 수 있습니다.테스트가 성공하면 자동 갱신도 문제없이 진행될 가능성이 높습니다.
-
sudo certbot renew --dry-run
- Standalone 모드의 갱신 시 주의점: standalone 모드는 갱신 시에도 Certbot이 80번 포트(또는 443번)를 사용해야 합니다. 따라서 자동 갱신이 실행되는 시점에 해당 포트를 사용하는 웹 서버(nghttpd, Nginx 등)가 실행 중이면 포트 충돌로 갱신이 실패할 수 있습니다.
- 해결 방안:
- 갱신 전에 서버 잠시 중지: Certbot 스크립트에 갱신 실행 전후로 웹 서버를 중지/시작하는 명령(Hook)을 추가합니다. (--pre-hook, --post-hook 옵션 사용)
- Webroot 방식으로 전환: 웹 서버를 중지하지 않아도 되는 webroot 방식으로 인증 방식을 변경하는 것을 고려합니다.
- 해결 방안:
5. 문제 해결 FAQ (Troubleshooting)
- 외부 접속 시 공유기 관리 페이지가 뜹니다:
- 1.1 항목 확인: 공유기 원격 관리 기능이 꺼져 있거나 포트가 변경되었는지 다시 확인하세요.
- 포트 포워딩, netsh 설정 다 했는데 외부 접속이 안 됩니다:
- WSL IP 주소가 변경되었는지 확인하고 netsh portproxy 규칙을 업데이트하세요. (자동화 스크립트 사용 권장)
- Windows 방화벽 또는 다른 보안 프로그램에서 TCP 80, 443 포트가 인바운드로 허용되었는지 확인하세요.
- ISP(인터넷 서비스 제공자)가 가정용 회선에서 80/443 포트를 차단하는 경우가 있습니다. 다른 포트(예: 8080, 8443)로 포트 포워딩 및 서버 설정을 변경하여 테스트해보세요.
- 공유기의 공인 IP 주소가 유동 IP인 경우, IP 변경 시 접속이 안 될 수 있습니다. DDNS(Dynamic DNS) 서비스를 이용하는 것을 고려하세요.
- WSL에서 sudo nghttpd ... 실행 시 Permission denied (Errno 13) 오류:
- 리눅스에서 1024번 미만 포트(Well-known ports)는 root 권한이 필요합니다. 명령어 앞에 sudo를 붙였는지 확인하세요.
- Certbot 갱신 시 포트 바인딩 에러 (Problem binding to port 80 등):
- Standalone 모드 갱신 시, 80번 포트를 이미 다른 웹 서버가 사용 중일 가능성이 높습니다. 위 4번 항목의 해결 방안을 참고하세요.
정리 및 요약
- ipTIME 공유기: 원격 관리 포트 변경/비활성화 후, 외부 80/443 포트를 Windows PC의 80/443 포트로 포트 포워딩 설정. (DHCP 고정 할당 권장)
- Windows: 방화벽에서 TCP 80/443 인바운드 허용 후, netsh interface portproxy 명령어를 사용하여 Windows의 80/443 포트를 WSL의 IP 주소와 80/443 포트로 포워딩. (WSL IP 변경에 대비한 자동화 스크립트 사용 권장)
- WSL (리눅스):
- certbot 설치 후 certonly --standalone 명령으로 Let's Encrypt 인증서 발급. (실행 전 80/443 포트 사용 중인 서버 중지)
- 발급받은 인증서 파일 경로(privkey.pem, fullchain.pem)를 확인.
- nghttpd (또는 Nginx/Apache 등)를 이용하여 HTTPS 서버 실행. (인증서 경로 지정, 443 포트 사용)
- certbot renew --dry-run으로 자동 갱신 테스트 및 포트 충돌 문제 고려.
이 과정을 통해 다소 복잡해 보일 수 있는 가정집의 다단계 네트워크 환경에서도 안정적인 개인 HTTPS(HTTP/2) 웹 서버를 성공적으로 운영할 수 있습니다. 즐거운 서버 운영 되시길 바랍니다!