서버 운영
Nginx proxy_pass에서 upstream이 엉뚱한 도메인을 받는 이유
Nginx로 리버스 프록시를 설정할 때 upstream 서버가 Host 헤더를 localhost로 받는 경우가 있다. proxy_set_header Host 설정 하나로 해결된다.
Nginx로 리버스 프록시를 구성한 뒤 upstream 앱 로그를 보니 요청의 Host 헤더가 localhost:3000으로 찍혀 있었다. 클라이언트는 분명 example.com으로 요청을 보냈는데. 이 상태에서 앱 안에서 리다이렉트 URL을 생성하거나 canonical 주소를 만들면 틀린 도메인이 들어간다.
왜 Host 헤더가 바뀌는가
Nginx의 proxy_pass를 별다른 설정 없이 쓰면 upstream으로 전달되는 Host 헤더가 proxy_pass에 지정한 주소로 바뀐다.
location / {
proxy_pass http://localhost:3000;
# upstream이 받는 Host: localhost:3000
}
클라이언트가 example.com으로 요청을 보냈어도 upstream 앱은 Host를 localhost:3000으로 받는다.
원본 Host를 전달하는 방법
location / {
proxy_pass http://localhost:3000;
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;
}
$host는 클라이언트 요청에서 온 Host 헤더 값이다. 이렇게 설정하면 upstream이 example.com을 그대로 받는다.
X-Forwarded-For와 X-Forwarded-Proto도 같이 전달하지 않으면 앱에서 실제 클라이언트 IP나 프로토콜(https/http)을 알 수 없다. 로그에 모든 요청 IP가 127.0.0.1로 찍히는 것도 이 때문이다.
HTTPS 리다이렉트가 루프에 빠지는 경우
Next.js나 Express에서 HTTPS 강제 리다이렉트를 X-Forwarded-Proto를 보고 판단하도록 구현하는 경우가 많다. Nginx가 이 헤더를 전달하지 않으면 앱은 항상 http로 요청이 왔다고 판단해서 https로 계속 리다이렉트하고, 브라우저가 리다이렉트 루프 오류를 띄운다.
설정을 바꾼 뒤에는 반드시 설정 문법을 검사하고 리로드한다.
sudo nginx -t && sudo nginx -s reload
그리고 upstream 앱 로그에서 Host와 X-Forwarded-Proto가 의도한 값으로 찍히는지 확인한다. curl -v https://example.com으로 응답 헤더를 보는 것보다 upstream 쪽 로그를 직접 보는 게 더 확실하다.