백엔드가 http://192.168.0.10:8080/guacamole/ 처럼 서브 경로에 설치돼 있는데 외부에서는 https://app.example.com/ 루트로 접속시키고 싶은 상황이다. NPM 의 기본 화면에는 경로를 넣는 칸이 없으므로 Advanced 탭에 location 블록을 직접 쓴다.
proxy_pass 뒤에 경로가 붙어 있는지에 따라 동작이 완전히 달라진다. 이것 하나가 대부분의 사고 원인이다.
| 설정 | 요청 /login 이 백엔드로 갈 때 |
|---|---|
proxy_pass http://backend; |
/login (요청 URI 그대로) |
proxy_pass http://backend/; |
/login (location 접두사를 떼고 / 에 이어 붙임) |
proxy_pass http://backend/guacamole; |
/guacamole/login |
proxy_pass http://backend/guacamole/; |
/guacamole/login (location 접두사 제거 후 이어 붙임) |
location / 에서는 접두사가 / 뿐이라 두 형태의 차이가 작지만, 슬래시가 겹쳐 //login 같은 경로가 만들어지면 백엔드가 404 나 500 을 낸다. 애매하면 로그에 실제로 어떤 경로가 도착했는지 확인한다.
# 백엔드 접근 로그에서 실제 요청 경로를 본다
docker logs <backend_container> --tail 50
nginx: [emerg] location "/" is outside location "/guacamole" in /data/nginx/proxy_host/46.conf:65
NPM 의 Custom Location 에 이미 /guacamole 블록이 만들어져 있는데 Advanced 에 location / 를 또 넣어 생긴 충돌이다. NPM 이 생성하는 설정 안에 내 블록이 끼어들기 때문에 중첩이 어긋난다.
정리 방법은 둘 중 하나다.
location 을 전부 쓴다.두 곳에 나눠 쓰지 않는 것이 요령이다.
location / {
proxy_pass http://192.168.0.10:8080/guacamole/;
proxy_http_version 1.1;
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;
# 원격 화면은 WebSocket 을 쓴다
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off;
}
Guacamole 은 터널링에 WebSocket 을 쓰므로 업그레이드 헤더와 긴 proxy_read_timeout 이 없으면 로그인 후 화면에서 끊긴다.
Nextcloud 는 자기 URL 을 스스로 만들어 내므로 프록시 설정만으로는 끝나지 않는다. config.php 를 함께 맞춰야 한다.
location / {
proxy_pass http://192.168.0.10:8000/apps/nextcloud;
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;
proxy_set_header X-Forwarded-Host $host;
proxy_request_buffering off;
client_max_body_size 10G;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }
<?php
$CONFIG = array (
'trusted_domains' => array (
0 => 'drive.example.com',
),
'trusted_proxies' => array (
0 => '172.29.0.1',
),
'forwarded_for_headers' => array (
0 => 'HTTP_X_FORWARDED_FOR',
),
'overwritehost' => 'drive.example.com',
'overwriteprotocol' => 'https',
'overwritewebroot' => '/',
'overwrite.cli.url' => 'https://drive.example.com',
'default_phone_region' => 'KR',
);
공식 문서 기준으로 overwritewebroot 는 프록시가 Nextcloud 를 노출하는 절대 웹 경로다. 내부 설치 경로가 아니다. 루트로 서비스하면 / 이며, 이 값이 내부 경로(/apps/nextcloud)로 남아 있으면 로그인 직후 리다이렉트가 어긋나 303 이 반복되거나 500 이 난다. 정적 파일은 잘 나오는데 로그인만 안 되는 증상이면 이 값을 먼저 본다.
trusted_proxies 에는 Nextcloud 입장에서 보이는 프록시의 주소를 넣는다. 도커 브리지를 통해 들어오면 그 게이트웨이 주소다. 값이 틀리면 접근 로그에 모든 사용자가 같은 내부 IP 로 기록된다.
overwrite.cli.url 은 https 로 맞춘다. http 로 남으면 일부 링크와 알림이 평문 URL 로 만들어진다.
애플리케이션이 서브 경로를 지원하지 않거나 리다이렉트가 복잡하면, 외부에서도 같은 경로로 접속시키는 편이 훨씬 단순하다.
location /guacamole/ {
proxy_pass http://192.168.0.10:8080/guacamole/;
# 헤더 설정 동일
}
주소가 조금 길어지는 대신 경로 변환에서 오는 문제가 전부 사라진다.
docker exec -it npm nginx -t
curl -IL https://app.example.com/
curl -I https://app.example.com/static/ # 정적 자원이 404 면 경로 변환 문제
브라우저 개발자 도구의 Network 탭에서 404 가 나는 자원의 실제 URL 을 보면 어느 지점에서 경로가 어긋났는지 바로 드러난다.