AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.
Nginx Proxy Manager 홈서버 리버스 프록시 개념도

Nginx Proxy Manager 설치 가이드 – 홈서버 HTTPS 쉽게 적용하는 법

집에서 NAS나 미니PC로 서버를 운영하다 보면 한 가지 고민이 생깁니다. 여러 서비스를 돌리고 있는데, 각각 다른 포트 번호로 접속해야 하고, 브라우저에는 ‘안전하지 않은 사이트’라는 경고가 뜨죠. 예를 들어 Portainer는 9000번, Uptime Kuma는 3001번, 블로그는 8080번… 포트 번호를 외우는 것도 일이고, HTTPS 인증서를 서비스마다 따로 설정하자니 막막합니다.

이런 문제를 한 방에 해결해 주는 도구가 바로 Nginx Proxy Manager(이하 NPM)입니다. 이름만 들으면 어려워 보이지만, 실제로는 Nginx의 리버스 프록시 기능을 웹 GUI로 감싸 놓은 도구라서 터미널에서 설정 파일을 직접 편집할 필요가 전혀 없습니다. Docker만 설치되어 있다면 5분이면 띄울 수 있고, Let’s Encrypt 무료 SSL 인증서 발급과 자동 갱신까지 클릭 몇 번으로 끝납니다.

이 글에서는 Nginx Proxy Manager가 무엇인지, 왜 홈서버 운영에 필수적인지, 그리고 설치부터 실전 서비스 연결까지 모든 과정을 차근차근 안내합니다. Cloudflare Tunnel과의 차이점도 함께 다루니, 이미 터널을 쓰고 계신 분도 읽어 보시면 도움이 될 겁니다.

리버스 프록시 동작 원리 다이어그램

리버스 프록시가 뭔데, 왜 필요한 걸까

포워드 프록시 vs 리버스 프록시

프록시(Proxy)는 ‘대리’라는 뜻입니다. 우리가 흔히 아는 프록시는 포워드 프록시로, 클라이언트(내 PC) 앞에 서서 외부 서버에 대신 요청을 보내 주는 역할을 합니다. 회사에서 특정 사이트를 차단하거나, VPN처럼 IP를 숨길 때 쓰이죠.

리버스 프록시는 반대입니다. 서버 앞에 서서 외부 요청을 받아 뒤에 있는 여러 서비스에 적절히 전달하는 역할을 합니다. 쉽게 말하면 호텔 프런트 데스크와 같습니다. 손님(사용자)은 프런트(리버스 프록시)에만 말하면 되고, 프런트가 알아서 적절한 방(서비스)으로 안내해 줍니다.

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

홈서버를 운영하면 보통 하나의 IP 주소(또는 도메인)에 여러 서비스를 올리게 됩니다. 리버스 프록시 없이는 이런 상황이 됩니다.

  • blog.example.com:8080 – 블로그
  • blog.example.com:9000 – Portainer
  • blog.example.com:3001 – Uptime Kuma
  • blog.example.com:8888 – Jupyter Notebook

포트 번호를 매번 기억해서 입력해야 하고, 다른 사람에게 주소를 알려주기도 불편합니다. 리버스 프록시를 쓰면 이렇게 바뀝니다.

  • blog.example.com → 블로그
  • portainer.example.com → Portainer
  • uptime.example.com → Uptime Kuma
  • jupyter.example.com → Jupyter Notebook

모두 80/443 포트(HTTP/HTTPS 기본 포트)로 접속하니 포트 번호를 기억할 필요가 없고, 서브도메인만으로 깔끔하게 구분됩니다. 여기에 HTTPS까지 자동으로 적용되니 보안도 챙길 수 있습니다.

리버스 프록시가 제공하는 부가 기능들

  • SSL 종단(SSL Termination): 리버스 프록시에서 HTTPS를 처리하고, 뒤쪽 서비스에는 HTTP로 전달합니다. 각 서비스마다 인증서를 설정할 필요가 없습니다.
  • 접근 제어: 특정 서비스에 IP 화이트리스트나 기본 인증(Basic Auth)을 적용할 수 있습니다.
  • 캐싱과 압축: 정적 파일을 캐싱하고 응답을 gzip 압축하여 성능을 개선합니다.
  • 로드 밸런싱: 같은 서비스를 여러 인스턴스로 돌릴 때 트래픽을 분산합니다(홈서버에서는 자주 쓰이지 않지만 알아두면 좋습니다).

Nginx Proxy Manager 소개 – Nginx를 GUI로 쓰는 가장 쉬운 방법

Nginx Proxy Manager란

Nginx Proxy Manager는 오픈소스 프로젝트로, Nginx 웹 서버의 리버스 프록시 기능을 직관적인 웹 인터페이스로 제공합니다. 개발자 Jamie Curnow가 만들었고, GitHub에서 2만 개 이상의 스타를 받을 정도로 홈서버 커뮤니티에서 사실상 표준 도구로 자리 잡았습니다.

전통적으로 Nginx를 리버스 프록시로 쓰려면 /etc/nginx/conf.d/ 디렉터리에 설정 파일을 직접 작성하고, certbot으로 SSL 인증서를 발급받고, cron으로 갱신 스케줄을 잡아야 했습니다. 이 과정이 리눅스에 익숙하지 않은 분들에게는 큰 진입 장벽이었죠. NPM은 이 모든 것을 웹 화면에서 폼을 채우고 버튼을 클릭하는 것만으로 해결해 줍니다.

주요 기능 정리

  • 웹 기반 GUI: 프록시 호스트 추가, 수정, 삭제를 브라우저에서 바로 처리
  • Let’s Encrypt 자동 SSL: 도메인만 입력하면 무료 인증서 발급 + 자동 갱신
  • 리다이렉션 호스트: HTTP → HTTPS 강제 리다이렉트, 도메인 간 리다이렉트 설정
  • 스트림(TCP/UDP) 프록시: HTTP뿐 아니라 TCP/UDP 트래픽도 프록시 가능
  • 접근 제어 목록(Access List): IP 제한, 기본 인증(Basic Auth) 설정
  • 사용자 관리: 여러 관리자 계정 생성 가능
  • 커스텀 Nginx 설정: 고급 사용자를 위한 커스텀 설정 입력 지원

Cloudflare Tunnel과 뭐가 다를까

이전 포스팅에서 Cloudflare Tunnel을 소개한 적이 있죠. 둘 다 홈서버를 외부에 공개하는 데 쓸 수 있지만, 역할과 동작 방식이 다릅니다.

  • Cloudflare Tunnel: 공유기 포트포워딩 없이 Cloudflare 네트워크를 경유하여 서버를 외부에 노출합니다. 서버의 실제 IP가 노출되지 않고, DDoS 방어도 Cloudflare가 처리해 줍니다. 다만 모든 트래픽이 Cloudflare를 거치므로 대역폭 제한이나 정책 변경에 영향을 받을 수 있습니다.
  • Nginx Proxy Manager: 서버 내부에서 동작하며, 포트포워딩을 통해 직접 트래픽을 받습니다. 외부 서비스에 의존하지 않고, LAN 내부 서비스 간 프록시에도 활용됩니다.

실전에서는 둘을 함께 쓰는 경우도 많습니다. Cloudflare Tunnel로 외부 트래픽을 안전하게 받고, 내부에서는 NPM이 각 서비스로 트래픽을 분배하는 구조입니다. 외부 공개가 필요 없는 내부 서비스(예: Portainer, 관리 패널)는 NPM만으로 관리하면 됩니다.

사전 준비 – 도메인과 DNS 설정

도메인 준비

리버스 프록시를 제대로 활용하려면 도메인이 필요합니다. 서브도메인별로 다른 서비스를 연결할 것이기 때문이죠. 도메인이 없다면 아래 방법으로 준비할 수 있습니다.

  • 유료 도메인 구매: Namecheap, Cloudflare Registrar, 가비아 등에서 .com 도메인을 연간 1만 원 내외로 구매할 수 있습니다.
  • 무료 도메인: DuckDNS, No-IP 같은 무료 DDNS 서비스를 이용할 수도 있습니다. 다만 서브도메인 형태(yourid.duckdns.org)로만 쓸 수 있고, SSL 와일드카드 인증서 발급이 제한될 수 있습니다.

DNS 레코드 설정

도메인을 준비했다면, DNS에 A 레코드 또는 CNAME 레코드를 추가해야 합니다. 예를 들어 example.com 도메인을 소유하고 있고, 홈서버의 공인 IP가 123.456.78.90이라면 이렇게 설정합니다.

  • example.com → A 레코드 → 123.456.78.90
  • *.example.com → A 레코드 → 123.456.78.90 (와일드카드)

와일드카드(*) 레코드를 설정하면 portainer.example.com, uptime.example.com 등 서브도메인을 추가할 때마다 DNS를 건드릴 필요가 없어 편리합니다.

포트포워딩 설정

공유기에서 외부 포트 80과 443을 홈서버의 내부 IP로 포워딩해야 합니다. 공유기 관리 페이지(보통 192.168.0.1 또는 192.168.1.1)에 접속하여 포트포워딩(또는 가상 서버) 메뉴에서 설정합니다.

  • 외부 포트 80 → 내부 IP:80
  • 외부 포트 443 → 내부 IP:443

이 두 포트만 열면 NPM이 모든 트래픽을 받아 내부 서비스로 분배합니다. 각 서비스의 포트(9000, 3001 등)를 외부에 열 필요가 없으므로 오히려 보안이 좋아집니다.

DNS 레코드와 포트포워딩 설정 흐름

Docker Compose로 Nginx Proxy Manager 설치하기

docker-compose.yml 작성

NPM은 Docker 이미지로 제공되므로 설치가 매우 간단합니다. 먼저 적절한 디렉터리를 만들고 docker-compose.yml 파일을 작성합니다.

mkdir -p ~/npm && cd ~/npm

아래 내용으로 docker-compose.yml을 생성합니다.

version: '3.8'
services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    environment:
      TZ: 'Asia/Seoul'

각 포트의 역할을 설명하겠습니다.

  • 80: HTTP 트래픽 수신 (Let’s Encrypt 인증에도 사용)
  • 443: HTTPS 트래픽 수신
  • 81: NPM 관리자 웹 인터페이스

volumes에 지정된 경로에 설정 데이터와 SSL 인증서가 저장됩니다. 컨테이너를 삭제하고 다시 만들어도 데이터가 유지되므로 안심하세요.

컨테이너 실행

docker compose up -d

몇 초면 컨테이너가 올라옵니다. 로그를 확인하려면 docker compose logs -f를 실행하세요. 정상적으로 시작되면 아래와 같은 로그가 나타납니다.

[info] Server is ready on port 81

초기 로그인 및 설정

브라우저에서 http://서버IP:81로 접속합니다. 초기 로그인 정보는 다음과 같습니다.

첫 로그인 후 바로 이메일과 비밀번호를 변경하라는 화면이 나타납니다. 반드시 강력한 비밀번호로 변경하세요. NPM 관리 화면은 서버의 모든 프록시 설정을 제어할 수 있으므로, 보안이 매우 중요합니다.

비밀번호를 변경하면 깔끔한 대시보드가 나타납니다. 왼쪽 메뉴에 Proxy Hosts, Redirection Hosts, Streams, 404 Hosts, SSL Certificates, Access Lists 등의 항목이 보입니다.

실전 활용 – 서비스별 프록시 호스트 설정

기본 프록시 호스트 추가 (예: Uptime Kuma)

이미 Docker로 Uptime Kuma를 3001번 포트에서 운영하고 있다고 가정하겠습니다. uptime.example.com으로 접속할 수 있도록 프록시 호스트를 추가해 봅시다.

1단계: NPM 대시보드에서 Proxy HostsAdd Proxy Host를 클릭합니다.

2단계: Details 탭에서 다음을 입력합니다.

  • Domain Names: uptime.example.com
  • Scheme: http
  • Forward Hostname / IP: 서버 내부 IP (예: 192.168.0.10) 또는 Docker 네트워크를 사용한다면 컨테이너 이름
  • Forward Port: 3001
  • Block Common Exploits: 체크 (일반적인 웹 공격 패턴을 차단합니다)
  • Websockets Support: 체크 (Uptime Kuma는 WebSocket을 사용합니다)

3단계: SSL 탭으로 이동합니다.

  • SSL Certificate: Request a new SSL Certificate 선택
  • Force SSL: 체크 (HTTP로 접속하면 자동으로 HTTPS로 리다이렉트)
  • HTTP/2 Support: 체크
  • Email Address for Let’s Encrypt: 실제 이메일 주소 입력
  • I Agree to the Let’s Encrypt Terms of Service: 체크

4단계: Save를 클릭합니다. NPM이 자동으로 Let’s Encrypt에 인증서를 요청하고, Nginx 설정을 생성하고, 프록시를 활성화합니다. 보통 10초 이내에 완료됩니다.

이제 https://uptime.example.com으로 접속하면 Uptime Kuma가 HTTPS로 안전하게 열립니다. 브라우저 주소창에 자물쇠 아이콘이 표시되는 것을 확인할 수 있습니다.

NPM 프록시 호스트 설정 화면 예시

Docker 네트워크를 활용한 깔끔한 구성

여러 서비스를 Docker로 운영한다면, 같은 Docker 네트워크에 연결하여 컨테이너 이름으로 접근하는 것이 더 깔끔합니다. IP 주소가 변경될 걱정이 없고, 서비스 간 통신도 Docker 내부 네트워크에서 이루어져 보안상 유리합니다.

먼저 공용 Docker 네트워크를 생성합니다.

docker network create proxy-network

NPM의 docker-compose.yml에 네트워크를 추가합니다.

version: '3.8'
services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    environment:
      TZ: 'Asia/Seoul'
    networks:
      - proxy-network

networks:
  proxy-network:
    external: true

다른 서비스(예: Uptime Kuma)의 docker-compose.yml에도 같은 네트워크를 추가합니다.

version: '3.8'
services:
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    container_name: uptime-kuma
    restart: unless-stopped
    volumes:
      - ./data:/app/data
    networks:
      - proxy-network

networks:
  proxy-network:
    external: true

이렇게 하면 NPM 프록시 호스트 설정에서 Forward Hostname에 IP 대신 uptime-kuma(컨테이너 이름)를 입력할 수 있습니다. 서비스의 포트를 호스트에 바인딩(ports:)할 필요도 없어져서, 외부에서 직접 포트로 접속하는 것을 원천 차단할 수 있습니다.

여러 서비스 한꺼번에 구성하기

같은 방식으로 다른 서비스도 추가하면 됩니다. 실제로 자주 쓰는 구성을 예시로 보여드리겠습니다.

  • portainer.example.com → portainer:9000 (Websockets 체크)
  • uptime.example.com → uptime-kuma:3001 (Websockets 체크)
  • blog.example.com → wordpress:80
  • dify.example.com → dify-nginx:80
  • code.example.com → code-server:8080 (Websockets 체크)

각 프록시 호스트에서 SSL을 개별로 발급받아도 되지만, 와일드카드 인증서를 하나 발급받으면 관리가 더 편합니다. 와일드카드 인증서 설정 방법은 아래에서 설명하겠습니다.

Let’s Encrypt 와일드카드 SSL 인증서 발급

와일드카드 인증서란

일반 SSL 인증서는 uptime.example.com처럼 특정 도메인 하나에만 적용됩니다. 서비스를 추가할 때마다 새 인증서를 발급받아야 하죠. 와일드카드 인증서*.example.com 형태로, 모든 서브도메인에 하나의 인증서로 대응합니다.

DNS Challenge 방식으로 발급하기

와일드카드 인증서는 HTTP Challenge로는 발급할 수 없고, DNS Challenge를 사용해야 합니다. DNS Challenge는 도메인의 DNS 레코드에 특정 TXT 레코드를 추가하여 소유권을 증명하는 방식입니다.

NPM에서 SSL Certificates → Add SSL Certificate → Let’s Encrypt를 선택하고 다음을 입력합니다.

  • Domain Names: *.example.comexample.com 두 개를 추가
  • Use a DNS Challenge: 체크
  • DNS Provider: 사용 중인 DNS 서비스 선택 (Cloudflare, Route53, DigitalOcean 등 약 40개 지원)
  • Credentials File Content: DNS 서비스의 API 키 입력

예를 들어 Cloudflare를 사용한다면 Credentials에 다음과 같이 입력합니다.

dns_cloudflare_api_token = your-api-token-here

Cloudflare API 토큰은 Cloudflare 대시보드 → My Profile → API Tokens에서 생성할 수 있습니다. Zone DNS 편집 권한만 있으면 됩니다.

Save를 클릭하면 NPM이 자동으로 DNS TXT 레코드를 추가하고, Let’s Encrypt에서 인증서를 발급받고, 90일마다 자동으로 갱신합니다. 이후 프록시 호스트를 추가할 때 SSL Certificate 드롭다운에서 이 와일드카드 인증서를 선택하면 됩니다.

보안 강화 – 접근 제어와 추가 설정

Access List로 접근 제한하기

Portainer 같은 관리 도구는 아무나 접근하면 안 됩니다. NPM의 Access Lists 기능을 활용하면 IP 제한이나 기본 인증을 설정할 수 있습니다.

Access Lists 메뉴에서 Add Access List를 클릭합니다.

  • Name: Admin Only (알아보기 쉬운 이름)
  • Authorization 탭: 사용자명과 비밀번호를 추가 (기본 인증)
  • Access 탭: 허용할 IP 대역 추가 (예: 192.168.0.0/24 → Allow, deny all)

저장 후, 프록시 호스트 편집 화면의 Details 탭에서 Access List 드롭다운에서 방금 만든 목록을 선택합니다. 이제 해당 서비스에 접속하면 비밀번호를 묻거나, 허용되지 않은 IP에서는 403 오류를 반환합니다.

관리 화면(81번 포트) 보호

NPM 관리 화면 자체도 보호해야 합니다. 가장 간단한 방법은 81번 포트를 외부에 노출하지 않는 것입니다. docker-compose.yml의 포트 설정을 다음과 같이 변경하세요.

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

이렇게 하면 서버에 직접 접속하거나 SSH 터널을 통해서만 관리 화면에 접근할 수 있습니다. 또는 NPM 자체를 통해 npm.example.com으로 프록시하고, Access List를 적용하는 방법도 있습니다.

커스텀 Nginx 설정 활용

프록시 호스트의 Advanced 탭에서 Nginx 설정을 직접 입력할 수 있습니다. 자주 쓰는 설정 예시입니다.

업로드 파일 크기 제한 늘리기 (기본 1MB):

client_max_body_size 100m;

특정 경로만 프록시하기:

location /api {
    proxy_pass http://backend:3000;
}

보안 헤더 추가:

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";

트러블슈팅 – 자주 겪는 문제와 해결법

502 Bad Gateway

가장 흔한 오류입니다. NPM이 뒤쪽 서비스에 연결하지 못할 때 발생합니다.

  • 원인 1: Forward Hostname/IP 또는 Port가 잘못되었습니다. → 서비스가 실제로 해당 주소와 포트에서 동작하는지 확인하세요.
  • 원인 2: Docker 네트워크가 다릅니다. → NPM과 대상 서비스가 같은 Docker 네트워크에 있는지 확인하세요.
  • 원인 3: 서비스가 아직 시작되지 않았습니다. → docker ps로 컨테이너 상태를 확인하세요.

SSL 인증서 발급 실패

  • HTTP Challenge 실패: 포트 80이 외부에서 접근 가능한지 확인하세요. 공유기 포트포워딩과 방화벽 설정을 점검합니다.
  • DNS Challenge 실패: API 키가 올바른지, DNS Provider를 정확히 선택했는지 확인하세요. Cloudflare의 경우 API Token의 권한(Zone:DNS:Edit)을 확인합니다.
  • Rate Limit: Let’s Encrypt는 동일 도메인에 대해 주당 5회까지만 인증서를 발급합니다. 테스트 시에는 실패를 최소화하세요.

WebSocket 연결 실패

Uptime Kuma, Portainer, code-server 등 WebSocket을 사용하는 서비스에서 실시간 업데이트가 되지 않을 때는 프록시 호스트 설정에서 Websockets Support를 체크했는지 확인하세요.

느린 응답 속도

대용량 파일을 다루거나 미디어 스트리밍을 할 때 느려진다면, Advanced 탭에서 다음 설정을 추가해 보세요.

proxy_buffering off;
client_max_body_size 0;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;

실전 구성 예시 – 홈서버 전체 아키텍처

지금까지 배운 내용을 종합하여, 실제 홈서버에서 NPM을 중심으로 구성한 전체 아키텍처를 보여드리겠습니다.

NPM 중심 홈서버 전체 아키텍처

외부에서 *.example.com으로 요청이 들어오면, 공유기의 포트포워딩을 통해 서버의 80/443 포트로 전달됩니다. NPM은 요청의 도메인을 확인하여 적절한 Docker 컨테이너로 라우팅합니다. 모든 통신은 HTTPS로 암호화되고, 관리 도구에는 Access List가 적용되어 있습니다.

이 구조의 장점은 새 서비스를 추가할 때 Docker 컨테이너를 띄우고, NPM에서 프록시 호스트 하나만 추가하면 끝이라는 점입니다. DNS에 와일드카드 레코드를 설정해 두었다면 DNS조차 건드릴 필요가 없습니다. 5분이면 새 서비스에 HTTPS가 적용된 깔끔한 도메인을 붙일 수 있습니다.

마무리 – 홈서버 운영의 필수 인프라

Nginx Proxy Manager는 홈서버를 운영하는 분이라면 가장 먼저 설치해야 할 도구 중 하나입니다. Docker 환경에서 5분이면 설치가 끝나고, 웹 GUI에서 클릭 몇 번으로 리버스 프록시와 HTTPS를 설정할 수 있습니다.

정리하면 NPM을 써야 하는 이유는 세 가지입니다.

  • 편의성: 포트 번호 대신 서브도메인으로 깔끔하게 접속
  • 보안: 무료 SSL 인증서로 모든 통신을 HTTPS로 암호화
  • 관리 효율: 새 서비스 추가가 5분이면 끝나고, 인증서 갱신도 자동

이미 Docker로 Portainer나 Uptime Kuma, Pi-hole 같은 서비스를 운영하고 계시다면, NPM을 추가하여 모든 서비스를 서브도메인으로 통합해 보세요. 홈서버 운영이 한 단계 더 편리해지고 전문적으로 바뀌는 것을 체감하실 수 있을 겁니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.

답글 남기기

Your email address will not be published. Required fields are marked *.

Warning: Undefined array key "cookies" in /var/www/html/wp-content/themes/personal-cv-resume/class/class-post-related.php on line 212
*
*

최신 댓글