# Nginx Proxy Manager 완벽 가이드: 리버스 프록시로 홈서버 깔끔하게 정리하기
Table of Contents
“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 다른 리버스 프록시
| 특성 | NPM | Traefik | Caddy | 순정 Nginx |
|---|---|---|---|---|
| 설정 방식 | Web GUI | Docker Labels/YAML | Caddyfile | nginx.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
# 디렉토리 생성 및 실행
mkdir -p /srv/npm/{data,letsencrypt}
docker compose up -d
초기 로그인
- 브라우저에서
http://서버IP:81접속 - 기본 계정으로 로그인:
- Email:
admin@example.com - Password:
changeme
- Email:
- 즉시 이메일과 비밀번호 변경 (보안상 필수)
관리 UI 포트 보안
81 포트는 관리자 페이지입니다. 외부에서 접근할 필요가 없다면 포트를 로컬로 제한하세요:
ports:
- "80:80"
- "443:443"
- "127.0.0.1:81:81" # 로컬에서만 접근 가능
또는 나중에 NPM 자체를 통해 npm.mydomain.com으로 접근하도록 설정할 수 있습니다.
5. 프록시 호스트 설정하기
이제 실제로 서비스를 연결해봅시다.
예시로 Jellyfin을 jellyfin.mydomain.com으로 접속할 수 있게 설정합니다.
Step 1: 프록시 호스트 추가
- NPM 대시보드 → Hosts → Proxy Hosts
- 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 클릭하면 자동으로:
- Let’s Encrypt에서 SSL 인증서 발급
- Nginx 설정 생성
- 인증서 자동 갱신 스케줄 등록 (만료 전 자동 갱신)
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 생성
-
NPM 대시보드 → Access Lists → Add Access List
-
Details 탭:
Name: Internal Only
Authorization: (비워두면 IP 제한만)
Pass Auth to Host: Off
- Access 탭 (IP 기반 제어):
Satisfy: All
Allow:
┌─────────────────────────────────────────────┐
│ 192.168.0.0/24 # 내부 네트워크 허용 │
│ 10.0.0.0/8 # Tailscale 네트워크 │
└─────────────────────────────────────────────┘
Deny:
┌─────────────────────────────────────────────┐
│ all # 나머지는 모두 차단 │
└─────────────────────────────────────────────┘
- Save
비밀번호 보호 추가
민감한 서비스에 기본 인증(Basic Auth)을 추가할 수 있습니다.
-
Access Lists → Add Access List
-
Details 탭:
Name: Password Protected
- Authorization 탭:
Username: admin
Password: secure-password-here
- Save
Access List 적용
- Proxy Hosts → 해당 호스트 Edit
- Details 탭 → Access List 드롭다운에서 생성한 ACL 선택
- Save
사용 시나리오
┌─────────────────────────────────────────────────────────────┐
│ 접근 제어 활용 예시 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [서비스] [접근 제어] │
│ ───────────────────────────────────────────────────────── │
│ Jellyfin 공개 (가족/친구 사용) │
│ Nextcloud 공개 (개인 클라우드) │
│ Pi-hole Admin 내부만 + 비밀번호 │
│ Portainer 내부만 + 비밀번호 │
│ NPM 관리 페이지 내부만 │
│ Home Assistant 공개 (HA 자체 인증 있음) │
│ │
└─────────────────────────────────────────────────────────────┘
8. 고급 설정
Custom Locations
하나의 도메인에서 경로별로 다른 서비스로 연결할 수 있습니다.
예: mydomain.com/jellyfin → Jellyfin
mydomain.com/nextcloud → Nextcloud
- Proxy Host → Edit → Custom Locations 탭
- Add Location
Location: /jellyfin
Scheme: http
Forward Hostname: 192.168.0.10
Forward Port: 8096
주의: 모든 서비스가 경로 기반 프록시를 지원하는 것은 아닙니다. 대부분의 경우 서브도메인 방식이 더 안정적입니다.
스트림 프록시 (TCP/UDP)
HTTP가 아닌 TCP/UDP 서비스도 프록시할 수 있습니다.
- 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로 리다이렉트할 수 있습니다.
- 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
등록되지 않은 서브도메인 접속 시 표시할 페이지를 설정할 수 있습니다.
- Hosts → 404 Hosts → Add 404 Host
9. Docker 네트워크 통합
NPM과 다른 서비스들이 Docker에서 실행 중이라면, 같은 네트워크에 연결하는 것이 좋습니다.
공용 Docker 네트워크 생성
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” 또는 인증서 발급 안 됨
해결책:
-
DNS 확인: 도메인이 올바른 IP를 가리키는지 확인
nslookup jellyfin.mydomain.com -
포트 확인: 80, 443 포트가 열려있는지 확인
# 외부에서 테스트 (https://www.yougetsignal.com/tools/open-ports/) -
Cloudflare 프록시: Cloudflare를 사용한다면 프록시(주황색 구름)를 잠시 끄기
-
방화벽: 서버 방화벽에서 80, 443 허용 확인
sudo ufw allow 80/tcp sudo ufw allow 443/tcp
502 Bad Gateway
증상: 프록시 설정 후 502 에러
해결책:
-
백엔드 서비스 확인: 타겟 서비스가 실행 중인지 확인
docker ps | grep jellyfin curl http://192.168.0.10:8096 # 직접 접속 테스트 -
Forward Hostname 확인: IP 또는 컨테이너 이름이 정확한지 확인
-
네트워크 확인: Docker 네트워크가 연결되어 있는지 확인
docker network inspect proxy-network -
포트 확인: Forward Port가 정확한지 확인
WebSocket 연결 실패
증상: 실시간 기능이 동작하지 않음 (알림, 재생 상태 등)
해결책:
- Websockets Support 체크: Proxy Host 설정에서 활성화
- 서비스 재시작: NPM과 해당 서비스 재시작
docker restart nginx-proxy-manager docker restart jellyfin
무한 리다이렉트
증상: ERR_TOO_MANY_REDIRECTS
해결책:
- Force SSL 설정 확인: 이미 HTTPS인 서비스에 Force SSL을 켜면 문제 발생 가능
- 백엔드 서비스 설정: 서비스 자체에서 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) 정기적인 업데이트:
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) 적용
이런 고급 주제들도 다룰 예정입니다.
:::tip[NPM vs Traefik, 언제 바꿔야 할까?] NPM으로 충분히 운영할 수 있습니다. 하지만 다음 상황이라면 Traefik을 고려해보세요:
- Docker 컨테이너가 자주 추가/삭제됨 (Traefik은 자동 감지)
- 설정을 Git으로 버전 관리하고 싶음 (Traefik은 파일 기반)
- 더 세밀한 미들웨어 체인이 필요함
대부분의 홈서버 사용자에게는 NPM이 최적의 선택입니다. :::