이 글의 목차

“jellyfin.mydomain.com, nextcloud.mydomain.com, pihole.mydomain.com…”

홈서버에 서비스가 늘어날수록 관리가 복잡해집니다. 각 서비스마다 다른 포트 번호를 기억해야 하고, 포트포워딩 규칙도 서비스 개수만큼 늘어납니다. HTTPS를 적용하려면 서비스마다 인증서를 따로 관리해야 할 것 같고…

이 모든 문제를 한 방에 해결하는 것이 바로 리버스 프록시입니다. 그리고 홈서버 환경에서 리버스 프록시를 가장 쉽게 구축할 수 있는 도구가 **Nginx Proxy Manager(NPM)**입니다.

오늘 이 글을 따라하고 나면:

  • 모든 서비스를 서비스명.mydomain.com 형태로 접속할 수 있게 됩니다
  • 포트 번호를 외울 필요가 없어집니다
  • HTTPS가 원클릭으로 적용됩니다
  • 특정 서비스에 비밀번호 보호를 걸 수 있습니다

1. 리버스 프록시란?

프록시의 기본 개념

**프록시(Proxy)**는 “대리인”이라는 뜻입니다. 클라이언트와 서버 사이에서 요청을 대신 전달해주는 중간자 역할을 합니다.

프록시에는 두 종류가 있습니다:

포워드 프록시 (Forward Proxy)

  • 클라이언트 측에서 동작
  • 사용자의 요청을 대신 전달
  • 예: 회사에서 인터넷 접속 시 거치는 프록시, VPN

리버스 프록시 (Reverse Proxy)

  • 서버 측에서 동작
  • 외부 요청을 받아서 내부 서버로 전달
  • 예: 오늘 배울 Nginx Proxy Manager
┌─────────────────────────────────────────────────────────────┐
│ 프록시의 두 가지 종류 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [포워드 프록시] │
│ │
│ 사용자 ──→ 프록시 ──→ 인터넷 │
│ (클라이언트 측) │
│ "나 대신 요청해줘" │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ [리버스 프록시] │
│ │
│ 인터넷 ──→ 프록시 ──→ 서버들 │
│ (서버 측) │
│ "요청을 적절한 서버로 전달해줄게" │
│ │
└─────────────────────────────────────────────────────────────┘

리버스 프록시가 필요한 이유

홈서버에 여러 서비스가 있다고 가정해봅시다:

  • Jellyfin: 8096 포트
  • Nextcloud: 8080 포트
  • Pi-hole: 80 포트
  • Vaultwarden: 8222 포트
  • Home Assistant: 8123 포트

리버스 프록시 없이 운영하면:

포트포워딩 규칙 5개 필요
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
:8096 → Jellyfin :8080 → Nextcloud :80 → Pi-hole
:8222 → Vaultwarden :8123 → Home Assistant
접속 방법:
- http://mydomain.com:8096 (Jellyfin)
- http://mydomain.com:8080 (Nextcloud)
- http://mydomain.com:8222 (Vaultwarden)
- ...
문제점:
- 포트 번호를 다 외워야 함
- 포트포워딩 규칙이 서비스마다 필요
- HTTPS 적용이 서비스마다 따로
- 일부 포트는 ISP가 차단할 수 있음

리버스 프록시를 사용하면:

포트포워딩 규칙 2개만 필요
(80, 443)
│
▼
┌─────────────────┐
│ Nginx Proxy │
│ Manager │
│ (리버스 프록시) │
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
jellyfin. nextcloud. pihole.
mydomain.com mydomain.com mydomain.com
│ │ │
▼ ▼ ▼
localhost:8096 localhost:8080 localhost:80
접속 방법:
- https://jellyfin.mydomain.com
- https://nextcloud.mydomain.com
- https://pihole.mydomain.com
- ...
장점:
- 포트 번호 외울 필요 없음
- 포트포워딩은 80, 443 두 개만
- SSL 인증서 한 곳에서 통합 관리
- 접근 제어(ACL)를 프록시에서 처리

리버스 프록시의 핵심 기능

1) 요청 라우팅

  • 도메인/경로에 따라 적절한 백엔드 서버로 요청 전달
  • jellyfin.mydomain.com → Jellyfin 컨테이너
  • nextcloud.mydomain.com → Nextcloud 컨테이너

2) SSL/TLS 종단 (SSL Termination)

  • 클라이언트와의 HTTPS 암호화를 프록시가 처리
  • 내부 서비스는 HTTP로 통신해도 됨 (설정 간소화)

3) 로드 밸런싱

  • 여러 서버에 요청을 분산 (홈서버에서는 잘 안 씀)

4) 캐싱

  • 자주 요청되는 콘텐츠를 캐싱하여 속도 향상

5) 보안 계층

  • 백엔드 서버를 외부에 직접 노출하지 않음
  • 추가 인증 계층 적용 가능

2. Nginx Proxy Manager 소개

NPM이란?

**Nginx Proxy Manager(NPM)**는 Nginx를 웹 GUI로 쉽게 관리할 수 있게 만든 도구입니다.

Nginx는 원래 설정 파일을 직접 편집해야 하는 전문가용 도구입니다. NPM은 이 복잡한 설정을 웹 인터페이스에서 클릭 몇 번으로 할 수 있게 만들었습니다.

핵심 기능

  • 웹 기반 GUI: 브라우저에서 모든 설정 가능
  • Let’s Encrypt 통합: SSL 인증서 자동 발급 및 갱신
  • 프록시 호스트 관리: 서브도메인별 라우팅 설정
  • 접근 제어: IP 차단, 비밀번호 보호
  • 스트림 프록시: TCP/UDP 포워딩 (SSH, 게임 서버 등)
  • 리다이렉션: URL 리다이렉트 설정

NPM vs 다른 리버스 프록시

특성NPMTraefikCaddy순정 Nginx
설정 방식Web GUIDocker Labels/YAMLCaddyfilenginx.conf
학습 곡선매우 낮음중간낮음높음
Let’s Encrypt원클릭자동자동수동/Certbot
Docker 통합수동 설정자동 감지수동 설정수동 설정
리소스 사용중간낮음낮음낮음
홈서버 추천입문자중급자중급자전문가

결론: 홈서버 입문자에게는 NPM이 가장 쉽습니다. 나중에 더 세밀한 제어가 필요해지면 Traefik이나 Caddy로 마이그레이션할 수 있습니다.


3. 사전 준비사항

NPM을 설정하기 전에 다음이 준비되어 있어야 합니다:

체크리스트

[ ] 도메인 보유 (필수)
- 무료: DuckDNS (xxx.duckdns.org)
- 유료: 가비아, Namecheap 등에서 구매
[ ] DNS 설정 완료
- A 레코드: mydomain.com → 공인 IP
- CNAME 또는 A 레코드: *.mydomain.com → 공인 IP (와일드카드)
[ ] 포트포워딩 설정
- 80 포트 → NPM이 설치된 서버
- 443 포트 → NPM이 설치된 서버
[ ] Docker 설치 완료

DNS 설정 예시

DuckDNS 사용 시: DuckDNS는 서브도메인을 직접 지원하지 않습니다. 하지만 myhome.duckdns.org를 A 레코드로 등록하면 NPM에서 호스트명으로 사용할 수 있습니다.

커스텀 도메인 사용 시 (Cloudflare 예시):

DNS Records for mydomain.com:
Type Name Content Proxy
─────────────────────────────────────────────────────
A @ 121.134.56.78 Off
A * 121.134.56.78 Off
(와일드카드: 모든 서브도메인을 같은 IP로)
또는 개별 설정:
A jellyfin 121.134.56.78 Off
A nextcloud 121.134.56.78 Off
A pihole 121.134.56.78 Off

중요: Cloudflare를 사용할 경우, 처음 설정할 때는 프록시(주황색 구름)를 꺼두세요. NPM의 Let’s Encrypt 인증서 발급이 실패할 수 있습니다. 설정이 완료된 후에 프록시를 켜도 됩니다.


4. Nginx Proxy Manager 설치

Docker Compose로 설치

docker-compose.yml
services:
npm:
container_name: nginx-proxy-manager
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "81:81" # 관리자 웹 UI
volumes:
- /srv/npm/data:/data
- /srv/npm/letsencrypt:/etc/letsencrypt
environment:
- TZ=Asia/Seoul
Terminal window
# 디렉토리 생성 및 실행
mkdir -p /srv/npm/{data,letsencrypt}
docker compose up -d

초기 로그인

  1. 브라우저에서 http://서버IP:81 접속
  2. 기본 계정으로 로그인:
    • Email: admin@example.com
    • Password: changeme
  3. 즉시 이메일과 비밀번호 변경 (보안상 필수)

관리 UI 포트 보안

81 포트는 관리자 페이지입니다. 외부에서 접근할 필요가 없다면 포트를 로컬로 제한하세요:

ports:
- "80:80"
- "443:443"
- "127.0.0.1:81:81" # 로컬에서만 접근 가능

또는 나중에 NPM 자체를 통해 npm.mydomain.com으로 접근하도록 설정할 수 있습니다.


5. 프록시 호스트 설정하기

이제 실제로 서비스를 연결해봅시다. 예시로 Jellyfin을 jellyfin.mydomain.com으로 접속할 수 있게 설정합니다.

Step 1: 프록시 호스트 추가

  1. NPM 대시보드 → Hosts → Proxy Hosts
  2. Add Proxy Host 클릭

Step 2: Details 탭 설정

Domain Names:
┌─────────────────────────────────────────────┐
│ jellyfin.mydomain.com │
└─────────────────────────────────────────────┘
Scheme: http (내부 서비스가 HTTP일 경우)
Forward Hostname / IP:
┌─────────────────────────────────────────────┐
│ 192.168.0.10 (또는 컨테이너 이름: jellyfin) │
└─────────────────────────────────────────────┘
Forward Port:
┌─────────────────────────────────────────────┐
│ 8096 │
└─────────────────────────────────────────────┘
옵션:
[x] Cache Assets # 정적 파일 캐싱
[x] Block Common Exploits # 일반적인 공격 차단
[ ] Websockets Support # WebSocket 사용 시 체크 (Jellyfin은 필요)

Websockets Support: 실시간 통신이 필요한 서비스는 반드시 체크

  • Jellyfin: 필요 (실시간 재생 상태)
  • Nextcloud: 필요 (실시간 알림)
  • Home Assistant: 필요
  • Vaultwarden: 필요

Step 3: SSL 탭 설정 (중요)

SSL Certificate:
┌─────────────────────────────────────────────┐
│ Request a new SSL Certificate │
└─────────────────────────────────────────────┘
옵션:
[x] Force SSL # HTTP → HTTPS 자동 리다이렉트
[x] HTTP/2 Support # 속도 향상
[ ] HSTS Enabled # 보안 강화 (주의: 신중히 사용)
[ ] HSTS Subdomains
Email Address for Let's Encrypt:
┌─────────────────────────────────────────────┐
│ your-email@example.com │
└─────────────────────────────────────────────┘
[x] I Agree to the Let's Encrypt Terms of Service

Save 클릭하면 자동으로:

  1. Let’s Encrypt에서 SSL 인증서 발급
  2. Nginx 설정 생성
  3. 인증서 자동 갱신 스케줄 등록 (만료 전 자동 갱신)

Step 4: 접속 테스트

브라우저에서 https://jellyfin.mydomain.com 접속

  • 자물쇠 아이콘 확인 (HTTPS 적용됨)
  • Jellyfin 페이지가 정상적으로 표시되는지 확인

6. 주요 서비스별 설정 가이드

Jellyfin / Plex

Forward Hostname: jellyfin (또는 IP)
Forward Port: 8096 (Jellyfin) / 32400 (Plex)
[x] Websockets Support ← 반드시 체크!
[x] Block Common Exploits

Nextcloud

Nextcloud는 추가 설정이 필요합니다.

NPM 설정:

Forward Hostname: nextcloud (또는 IP)
Forward Port: 80 (또는 커스텀 포트)
[x] Websockets Support
[x] Block Common Exploits

Custom Nginx Configuration (Advanced 탭):

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 10G; # 대용량 파일 업로드 허용
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;

Nextcloud config.php 수정 필요:

'trusted_proxies' => ['172.16.0.0/12', '192.168.0.0/16'], # Docker 네트워크
'overwriteprotocol' => 'https',

Home Assistant

Forward Hostname: homeassistant (또는 IP)
Forward Port: 8123
[x] Websockets Support ← 필수!

Home Assistant configuration.yaml 추가:

http:
use_x_forwarded_for: true
trusted_proxies:
- 172.16.0.0/12
- 192.168.0.0/16

Vaultwarden

Forward Hostname: vaultwarden (또는 IP)
Forward Port: 80
[x] Websockets Support
[x] Force SSL ← 반드시! (비밀번호 관리자)

Pi-hole Admin

Forward Hostname: pihole (또는 IP)
Forward Port: 80
[ ] Websockets Support (불필요)

주의: Pi-hole 자체가 80 포트를 사용하면 NPM과 충돌합니다. Pi-hole을 다른 포트(예: 8053)로 변경하거나, 다른 서버에 설치하세요.

Portainer

Forward Hostname: portainer (또는 IP)
Forward Port: 9000
[x] Websockets Support

7. 접근 제어 설정 (Access Lists)

NPM의 강력한 기능 중 하나는 **접근 제어 목록(ACL)**입니다. 특정 서비스에 추가 보안 계층을 적용할 수 있습니다.

Access List 생성

  1. NPM 대시보드 → Access Lists → Add Access List

  2. Details 탭:

Name: Internal Only
Authorization: (비워두면 IP 제한만)
Pass Auth to Host: Off
  1. Access 탭 (IP 기반 제어):
Satisfy: All
Allow:
┌─────────────────────────────────────────────┐
│ 192.168.0.0/24 # 내부 네트워크 허용 │
│ 10.0.0.0/8 # Tailscale 네트워크 │
└─────────────────────────────────────────────┘
Deny:
┌─────────────────────────────────────────────┐
│ all # 나머지는 모두 차단 │
└─────────────────────────────────────────────┘
  1. Save

비밀번호 보호 추가

민감한 서비스에 기본 인증(Basic Auth)을 추가할 수 있습니다.

  1. Access Lists → Add Access List

  2. Details 탭:

Name: Password Protected
  1. Authorization 탭:
Username: admin
Password: secure-password-here
  1. Save

Access List 적용

  1. Proxy Hosts → 해당 호스트 Edit
  2. Details 탭 → Access List 드롭다운에서 생성한 ACL 선택
  3. Save

사용 시나리오

┌─────────────────────────────────────────────────────────────┐
│ 접근 제어 활용 예시 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [서비스] [접근 제어] │
│ ───────────────────────────────────────────────────────── │
│ Jellyfin 공개 (가족/친구 사용) │
│ Nextcloud 공개 (개인 클라우드) │
│ Pi-hole Admin 내부만 + 비밀번호 │
│ Portainer 내부만 + 비밀번호 │
│ NPM 관리 페이지 내부만 │
│ Home Assistant 공개 (HA 자체 인증 있음) │
│ │
└─────────────────────────────────────────────────────────────┘

8. 고급 설정

Custom Locations

하나의 도메인에서 경로별로 다른 서비스로 연결할 수 있습니다.

예: mydomain.com/jellyfin → Jellyfin mydomain.com/nextcloud → Nextcloud

  1. Proxy Host → Edit → Custom Locations 탭
  2. Add Location
Location: /jellyfin
Scheme: http
Forward Hostname: 192.168.0.10
Forward Port: 8096

주의: 모든 서비스가 경로 기반 프록시를 지원하는 것은 아닙니다. 대부분의 경우 서브도메인 방식이 더 안정적입니다.

스트림 프록시 (TCP/UDP)

HTTP가 아닌 TCP/UDP 서비스도 프록시할 수 있습니다.

  1. Hosts → Streams → Add Stream

예: SSH 접속을 2222 포트로:

Incoming Port: 2222
Forward Host: 192.168.0.10
Forward Port: 22
TCP Forwarding: On
UDP Forwarding: Off

예: 게임 서버:

Incoming Port: 25565 (Minecraft)
Forward Host: 192.168.0.10
Forward Port: 25565
TCP Forwarding: On

리다이렉션 호스트

특정 도메인을 다른 URL로 리다이렉트할 수 있습니다.

  1. Hosts → Redirection Hosts → Add Redirection Host
Domain Names: www.mydomain.com
Scheme: https
Domain Name: mydomain.com
Preserve Path: On

이렇게 하면 www.mydomain.com/path가 mydomain.com/path로 리다이렉트됩니다.

404 Host

등록되지 않은 서브도메인 접속 시 표시할 페이지를 설정할 수 있습니다.

  1. Hosts → 404 Hosts → Add 404 Host

9. Docker 네트워크 통합

NPM과 다른 서비스들이 Docker에서 실행 중이라면, 같은 네트워크에 연결하는 것이 좋습니다.

공용 Docker 네트워크 생성

Terminal window
docker network create proxy-network

Docker Compose 수정

NPM docker-compose.yml:

services:
npm:
container_name: nginx-proxy-manager
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- /srv/npm/data:/data
- /srv/npm/letsencrypt:/etc/letsencrypt
networks:
- proxy-network
networks:
proxy-network:
external: true

다른 서비스 (예: Jellyfin) docker-compose.yml:

services:
jellyfin:
container_name: jellyfin
image: jellyfin/jellyfin
# ... 기타 설정 ...
networks:
- proxy-network
- default # 내부 네트워크도 유지하려면
networks:
proxy-network:
external: true

장점

같은 Docker 네트워크에 있으면:

  • IP 대신 컨테이너 이름으로 연결 가능 (jellyfin:8096)
  • IP가 바뀌어도 설정 변경 불필요
  • 내부 통신이 더 빠름

NPM에서 Forward Hostname을 jellyfin으로 설정하면 됩니다.


10. 문제 해결

SSL 인증서 발급 실패

증상: “Error: Validation Failed” 또는 인증서 발급 안 됨

해결책:

  1. DNS 확인: 도메인이 올바른 IP를 가리키는지 확인

    Terminal window
    nslookup jellyfin.mydomain.com
  2. 포트 확인: 80, 443 포트가 열려있는지 확인

    Terminal window
    # 외부에서 테스트 (https://www.yougetsignal.com/tools/open-ports/)
  3. Cloudflare 프록시: Cloudflare를 사용한다면 프록시(주황색 구름)를 잠시 끄기

  4. 방화벽: 서버 방화벽에서 80, 443 허용 확인

    Terminal window
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp

502 Bad Gateway

증상: 프록시 설정 후 502 에러

해결책:

  1. 백엔드 서비스 확인: 타겟 서비스가 실행 중인지 확인

    Terminal window
    docker ps | grep jellyfin
    curl http://192.168.0.10:8096 # 직접 접속 테스트
  2. Forward Hostname 확인: IP 또는 컨테이너 이름이 정확한지 확인

  3. 네트워크 확인: Docker 네트워크가 연결되어 있는지 확인

    Terminal window
    docker network inspect proxy-network
  4. 포트 확인: Forward Port가 정확한지 확인

WebSocket 연결 실패

증상: 실시간 기능이 동작하지 않음 (알림, 재생 상태 등)

해결책:

  1. Websockets Support 체크: Proxy Host 설정에서 활성화
  2. 서비스 재시작: NPM과 해당 서비스 재시작
    Terminal window
    docker restart nginx-proxy-manager
    docker restart jellyfin

무한 리다이렉트

증상: ERR_TOO_MANY_REDIRECTS

해결책:

  1. Force SSL 설정 확인: 이미 HTTPS인 서비스에 Force SSL을 켜면 문제 발생 가능
  2. 백엔드 서비스 설정: 서비스 자체에서 HTTPS를 강제하는지 확인

11. 보안 권장사항

반드시 해야 할 것

[x] NPM 관리자 비밀번호 변경
[x] 관리 페이지(81번 포트) 외부 노출 차단
[x] 모든 서비스에 Force SSL 적용
[x] Block Common Exploits 활성화
[x] 민감한 서비스에 Access List 적용

추가 보안 조치

1) NPM 관리 페이지를 NPM을 통해 접근:

  • npm.mydomain.com → localhost:81 프록시 설정
  • Access List로 내부 IP만 허용

2) Fail2ban 연동: NPM 로그를 Fail2ban으로 모니터링하여 무차별 대입 공격 차단

3) 정기적인 업데이트:

Terminal window
docker compose pull
docker compose up -d

12. 최종 구성 예시

모든 설정을 마친 후의 구조입니다:

┌─────────────────────────────────────────────────────────────┐
│ 완성된 홈서버 구조 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 인터넷 │
│ │ │
│ ▼ │
│ 공유기 (포트포워딩: 80, 443 → NPM) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Nginx Proxy Manager │ │
│ │ │ │
│ │ jellyfin.mydomain.com ──→ jellyfin:8096 │ │
│ │ nextcloud.mydomain.com ──→ nextcloud:80 │ │
│ │ pihole.mydomain.com ──→ pihole:80 (ACL: 내부만) │ │
│ │ vault.mydomain.com ──→ vaultwarden:80 │ │
│ │ ha.mydomain.com ──→ homeassistant:8123 │ │
│ │ portainer.mydomain.com ──→ portainer:9000 (ACL) │ │
│ │ │ │
│ │ SSL: Let's Encrypt 와일드카드 또는 개별 인증서 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 모든 서비스가 HTTPS로 보호됨 │
│ 민감한 서비스는 추가 인증 적용 │
│ │
└─────────────────────────────────────────────────────────────┘

13. 다음 단계

NPM으로 리버스 프록시를 구축했다면, 홈서버의 기초 인프라가 완성된 것입니다.

다음 포스팅 **“Tailscale: 0원으로 구축하는 메시 VPN”**에서는 포트포워딩 없이도 어디서든 안전하게 홈서버에 접속하는 방법을 다룹니다.

또한 이후 포스팅에서는:

  • Cloudflare Tunnel: CGNAT 환경에서도 안전한 외부 접속
  • Authentik: 모든 서비스에 통합 인증(SSO) 적용

이런 고급 주제들도 다룰 예정입니다.


함께 읽을 실전 가이드

502가 발생하면 Nginx Proxy Manager 502 진단에서 공유 네트워크, 내부 포트와 HTTP·HTTPS 설정을 점검하세요.

NEXT IN SERIES

외부에서 내 서버 접속하기: VPN vs Tunnel vs SSH 완벽 비교 가이드

다음 편 읽기 →
← 전체 글로 돌아가기