Nginx Reverse Proxy 설정 된 Nest.js서버에서 방문자의 원본 IP 조회
2024년 4월 18일 목요일
4분 소요
0회
문제
Nest.js 백엔드 서버에서 로깅을 하던 중 IP주소가 전부 내 nginx 로컬 ip주소로 찍히고 있었다.
문제 해결
nginx 설정
nginx
의 설정파일에 아래 내용을 입력하면 된다.
nginx.conf
1
2
3
4
5
6
7
# 프록시 서버쪽에
proxy_set_header X-Forwarded-For $remote_addr;
# 클라이언트쪽에
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Real-IP;
real_ip_recursive on;
프록시 서버쪽에
라고 적은 부분은 nginx proxy를 설정한 부분에 작성하면 되고, 클라이언트쪽에
라고 적은 부분은 내부에서 사용하고 있는 nginx가 별도로 있다면 그곳에 적으면 된다. 본인은 같은 nginx에서 관리하고 있기 때문에 그냥 같이 적어주었다.
- proxy_set_header X-Forwarded-For $remote_addr;
- 이 설정은 클라이언트의 실제 IP 주소를
X-Forwarded-For HTTP
헤더에 추가한다.$remote_addr
변수는 클라이언트의 IP 주소를 나타낸다.
- 이 설정은 클라이언트의 실제 IP 주소를
- set_real_ip_from 0.0.0.0/0;
0.0.0.0/0
는 모든 IP 주소를 의미하고, 모든 IP 주소에서 오는X-Real-IP
또는X-Forwarded-For
헤더의 값이 신뢰할 수 있는 것으로 간주하게 함.
- real_ip_header X-Real-IP;
- 실제 클라이언트의 IP 주소를 나타내는 데 사용되는 헤더를 지정합니다. 여기서
X-Real-IP
는 실제 사용자 IP 주소를 포함할 헤더이다.
- 실제 클라이언트의 IP 주소를 나타내는 데 사용되는 헤더를 지정합니다. 여기서
- real_ip_recursive on;
- Nginx는 지정된 헤더(
X-Real-IP
또는X-Forwarded-For
)에서 실제 클라이언트의 IP 주소를 재귀적으로 검색한다. 만약 여러 프록시를 통해 요청이 전달될 경우 유용하다.
- Nginx는 지정된 헤더(
Nest.js 서버 설정
Nest.js
내부적으로는 express.js
를 사용하게 설정해두었는데, 이 부분에도 문제가 있었다. 관련 자료
NestFactory.create
에 제너릭 타입인 NestExpressApplication
를 추가하면 app.set
을 사용할 수 있게 된다.
main.ts
1
2
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.set('trust proxy', true);
그 후 IP정보를 로깅하던 부분을 변경한다.
기존에는 request.ip
로 로깅을 시도했는데, 위에서 설정한 X-Real-IP
에서 IP정보를 가져오게 변경하였다.
response.interceptor.ts
1
2
3
const request = context.switchToHttp().getRequest<Request>();
const ip = request.headers['x-real-ip'] ?? request.ip;
this.logger.debug(`user-ip: ${ip}|request-path: ${request.path}`);
내용 추가
본인은 @nestjs/throttler
모듈을 사용하고 있다. 그런데 ThrottlerGuard
에서 IP요청 횟수를 기억해서 사용하기 때문에 프록시를 사용하는 경우에는 추가 설정이 필요하다고 한다. 그 설정이 아래 내용이다.
typescript
1
2
3
4
5
6
7
8
9
10
11
import { Injectable } from '@nestjs/common';
import { ThrottlerGuard } from '@nestjs/throttler';
import { Request } from 'express';
@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
// proxy 서버 뒤에서 작동하기 때문에 실제 IP를 가져오기 위한 코드
protected async getTracker(req: Request): Promise<string> {
return req.ips.length ? req.ips[0] : req.ip;
}
}
app.modules.ts
1
2
3
4
5
6
7
8
9
10
11
12
@Module({
imports: [
// ...
ThrottlerModule.forRoot(),
],
controllers: [AppController],
providers: [
// ...
// 전역으로 등록할 GUARD를 기본 ThrottlerGuard가 아닌 직접 코딩한 ThrottlerBehindProxyGuard로 설정한다.
{ provide: APP_GUARD, useClass: ThrottlerBehindProxyGuard },
],
})
Loading reactions