Nginx conf 프록시 설정
Axios baseURL 설정 방식
- 프론트엔드 소스 코드에
baseURL
를 설정하는 방식이다.
export const apiClient = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
})
장점:
- 구현이 간단하고 직관적
- 클라이언트 코드에서 API 엔드포인트 관리가 용이
- 개발 환경에서 빠른 테스트와 디버깅 가능
단점:
- CORS (Cross-Origin Resource Sharing) 이슈 발생 가능
- 프로덕션 환경에서 baseURL을 하드코딩하면 유연성이 떨어짐
- 클라이언트의 보안에 취약할 수 있음 (API 엔드포인트가 노출됨)
Nginx 프록시 설정 방식
- 프론트엔드 소스 코드에는 백엔드 API를 설정하지 않고, 웹 서버인 nginx에 프록시 기능을 사용하는 방식이다.
- 예를 들어 브라우저에
http://exam.com/api/v1/users
라는 요청을 하면 클라이언트 소스 코드에서 서버에 데이터를 요청하기 위해 내부적으로http://api-server/api/v1/users
라고 요청을 보내게 된다. - 이때 서버에 보낸 요청 URL은 개발자 도구에 노출된다. 그리고 서버로부터 응답이 오면 해당 결과에 따라 새로운 페이지를 호출하는 형태로 진행된다.
- 하지만 nginx의 프록시 설정을 사용하면 서버로 보내는 요청은 nginx 프록시를 통해 서버에 전달된다. 따라서 개발자 도구에 표시되지 않는다.
export const apiClient = axios.create({
})
- 백엔드 API 주소는 nginx.conf 파일에 proxy 설정으로 대체한다.
server {
listen 80;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
// http://localhost:8080/api/.. 요청이 오면 /api/가 매칭되어 실행되는 프록시 설정
location /api/ {
# 마지막 슬래시를 추가하면 location 에 정의한 경로가 무시되어 http://localhost:8080 로 전달된다.
# 마지막에 슬래시가 없으면 정의한 경로가 유지되어 http://localhost:8080/api/ 로 전달된다.
proxy_pass http://localhost:8080/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
장점:
- CORS 이슈 해결 (브라우저 요청이 아니기 때문에)
- 보안 강화 (실제 백엔드 서버 정보 숨김)
- 로드 밸런싱, 캐싱 등 추가 기능 구현 가능
- SSL/TLS 종단점 통합 관리 가능
- 환경별 설정 관리가 용이 (개발/스테이징/프로덕션)
단점:
- 추가적인 설정과 관리가 필요
- 프록시 서버 구성에 대한 지식 필요
- 약간의 성능 오버헤드 발생 가능
실제 프로덕션 환경에서는 대부분 Nginx 프록시 방식을 사용하는 것이 권장됩니다. 이는 보안, 확장성, 유지보수 측면에서 더 많은 이점을 제공하기 때문입니다. 개발 환경에서는 직접 baseURL을 설정하는 방식을 사용하고, 프로덕션 환경에서는 Nginx 프록시를 사용하는 하이브리드 접근방식도 많이 사용됩니다.
기본 프록시 설정
location /api/ {
proxy_pass http://localhost:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_set_header Content-Type application/json;
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;
}
설정 항목별 상세 설명
1. Location 블록
location /api/ {
/api/
로 시작하는 모든 요청에 대해 아래의 설정들을 적용- 끝의 슬래시(/)는 URL 매칭 패턴에 영향을 미침
/api/
는 정확히 이 경로로 시작하는 요청만 매칭/api
는 ‘api’로 시작하는 모든 경로를 매칭
2. Proxy Pass
proxy_pass http://localhost:8080/;
- 클라이언트 요청을 백엔드 서버로 전달
- 끝의 슬래시(/)가 URL 재작성에 영향:
- 있는 경우: location에서 매칭된 부분을 제거하고 나머지 경로만 전달
- 없는 경우: 전체 URL을 그대로 전달
3. WebSocket 지원 설정
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
- WebSocket 연결을 지원하기 위한 필수 설정
- HTTP/1.1 프로토콜 사용 명시
- WebSocket으로의 프로토콜 업그레이드 지원
4. 프록시 캐시 설정
proxy_cache_bypass $http_upgrade;
- WebSocket 연결 시 프록시 캐시를 우회
- 실시간 양방향 통신을 위해 필요
5. 타임아웃 설정
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_connect_timeout
: 백엔드 서버 연결 수립 제한 시간proxy_send_timeout
: 백엔드 서버로 요청 전송 제한 시간proxy_read_timeout
: 백엔드 서버로부터 응답 대기 제한 시간
6. 프록시 헤더 설정
proxy_set_header Content-Type application/json;
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;
Content-Type
: JSON 형식의 데이터 전송 명시X-Real-IP
: 실제 클라이언트 IP 전달X-Forwarded-For
: 클라이언트 및 중간 프록시 서버들의 IP 주소 체인X-Forwarded-Proto
: 원본 요청의 프로토콜(http/https) 정보
프록시 헤더 설정과 서버의 RemoteAddr()
우선 전제부터 정리하자면,
Host
헤더는 “요청이 어떤 도메인/호스트로 왔는지”를 나타내는 HTTP 헤더이고,request.getRemoteAddr()
(서블릿/JSP 등 Java 환경 기준)은 “실제 TCP 연결을 맺은 클라이언트(혹은 프록시)의 IP 주소”를 반환합니다.
즉,
proxy_set_header Host $host;
설정 유무는 백엔드 서버가 인식하는 ‘Host 헤더 값’(= 어떤 호스트로 요청이 왔는지) 에 영향을 미치지만,request.getRemoteAddr()
가 반환하는 값(= ‘IP 주소’)에는 영향을 주지 않습니다.
왜냐하면 getRemoteAddr()
는 결국 서버에 직접 연결된 소켓의 IP를 반환하기 때문입니다. Nginx가 리버스 프록시로 동작할 때는, 백엔드 서버와의 직접 연결은 대부분 “Nginx의 IP”가 됩니다.
1. 기본 동작
1) proxy_set_header
설정이 없을 때
아무 설정도 없다면,
- 백엔드 서버가 보는 Host 헤더
- 기본적으로 Nginx는
proxy_pass http://api-server/;
등의 설정에 기재된 호스트(api-server
)를 Host 헤더로 넘깁니다. - 즉, 백엔드 입장에서
request.getHeader("Host")
값을 찍어보면api-server
가 됩니다.
- 기본적으로 Nginx는
- 백엔드 서버에서
request.getRemoteAddr()
- 실제 연결은 Nginx(프록시) → 백엔드 로 맺어지므로, 백엔드 서버 입장에서는 Nginx 서버의 IP가 클라이언트 IP가 됩니다.
- 따라서
request.getRemoteAddr()
는Nginx 서버 IP
를 반환합니다. - 예:
192.168.0.10
(Nginx IP)
정리:
- Host 헤더:
api-server
- getRemoteAddr(): Nginx IP
2) proxy_set_header Host $host;
설정이 있을 때
예시:
location /api/ {
proxy_pass http://api-server/;
proxy_set_header Host $host;
}
$host
는 클라이언트가 원래 요청한 호스트명(예:exam.com
등)을 의미합니다.- 이 설정이 있으면, 백엔드 서버는
Host
헤더를exam.com
으로 넘겨받게 됩니다.
그러나 이 설정만으로는 IP 관련 설정(X-Real-IP
나 X-Forwarded-For
)을 건드리지 않았으므로,
- 백엔드 서버에서
request.getRemoteAddr()
- 여전히 Nginx와의 TCP 연결이므로,
request.getRemoteAddr()
는 Nginx IP를 반환합니다(바뀌지 않음).
- 여전히 Nginx와의 TCP 연결이므로,
정리:
- Host 헤더:
exam.com
- getRemoteAddr(): 여전히 Nginx IP
2. 만약 “원본 클라이언트 IP”를 받고 싶다면?
위와는 별개로, request.getRemoteAddr()
에서 진짜 사용자 IP(클라이언트의 공인 IP 등)를 받고 싶은 경우가 있습니다. 이 때는 Host 헤더가 아니라 X-Forwarded-For
(혹은 X-Real-IP
) 와 같은 헤더를 Nginx에서 설정해야 합니다.
예시:
location /api/ {
proxy_pass http://api-server/;
# 원본 Host 그대로
proxy_set_header Host $host;
# 원본 클라이언트의 IP를 전달
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- 이렇게 하면, 백엔드 서버는
request.getHeader("X-Real-IP")
또는request.getHeader("X-Forwarded-For")
안에 실제 클라이언트 IP가 들어있는 걸 확인할 수 있게 됩니다. - 다만 기본적으로
request.getRemoteAddr()
값 자체는 여전히 프록시(Nginx) IP로 남습니다. - Java/Tomcat/스프링에서는
X-Forwarded-For
를 자동으로 해석해 주는RemoteIpValve
(Tomcat)나 스프링의ForwardedHeaderFilter
등을 설정해야,request.getRemoteAddr()
가 원본 IP로 바뀌도록 지원할 수도 있습니다.
3. 요약
proxy_set_header Host $host;
설정의 유무는- 백엔드가 어떤 Host 헤더를 받느냐에만 영향을 준다.
- 즉, 백엔드에서
request.getHeader("Host")
값을 확인했을 때 달라진다. Host: exam.com
으로 갈지,Host: api-server
로 갈지가 달라지는 것.
request.getRemoteAddr()
(서버 단에서 직접 보는 IP)는- 설정과 무관하게 “Nginx(프록시)와 연결된 TCP 소켓의 IP” 이므로 기본적으로 Nginx IP가 나온다.
- 원본 클라이언트 IP를 보고 싶으면,
X-Real-IP
,X-Forwarded-For
를 세팅하고, 백엔드(서버) 측에서 이를 해석하는 설정을 추가로 해야 한다.
결론적으로,
- “
proxy_set_header Host $host;
설정을 추가했다/안 했다”에 따라 Host 헤더는 달라질 수 있지만, request.getRemoteAddr()
는 (추가적인 IP 전달 설정을 하지 않는 한) 변함없이 Nginx 프록시 IP를 반환한다는 점을 기억하시면 됩니다.
보안 헤더 설정
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
보안 헤더 상세 설명
1. X-Frame-Options
add_header X-Frame-Options "SAMEORIGIN" always;
- 클릭재킹 공격 방지
- 프레임 내 페이지 로드를 같은 출처로만 제한
- 옵션:
DENY
: 모든 프레임 내 로드 차단SAMEORIGIN
: 같은 출처만 허용ALLOW-FROM uri
: 특정 출처만 허용
2. X-XSS-Protection
add_header X-XSS-Protection "1; mode=block" always;
- 크로스 사이트 스크립팅(XSS) 공격 방지
- 브라우저의 내장 XSS 필터 활성화
- 옵션:
0
: 필터 비활성화1
: 필터 활성화1; mode=block
: 필터 활성화 및 페이지 로드 차단
3. X-Content-Type-Options
add_header X-Content-Type-Options "nosniff" always;
- MIME 타입 스니핑 방지
- 선언된 콘텐츠 타입으로만 리소스 해석
- 브라우저가 콘텐츠 타입을 추측하지 못하도록 함
4. Referrer-Policy
add_header Referrer-Policy "no-referrer-when-downgrade" always;
- 리퍼러 정보 전송 정책 설정
no-referrer-when-downgrade
: HTTPS → HTTP로 이동 시 리퍼러 정보 미전송- 다른 옵션:
no-referrer
: 리퍼러 정보 미전송same-origin
: 같은 출처만 전송strict-origin
: HTTPS → HTTPS만 전송
5. Content-Security-Policy
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
- 웹 애플리케이션의 리소스 로드 정책 설정
- XSS 및 기타 주입 공격 방지
- 설정값 설명:
'self'
: 같은 출처의 리소스만 허용http: https:
: HTTP/HTTPS 프로토콜 허용data:
: data URL 허용blob:
: Blob URL 허용'unsafe-inline'
: 인라인 스크립트/스타일 허용
공통 속성
모든 보안 헤더에 사용된 always
키워드:
- 응답 코드와 관계없이 항상 헤더 추가
- 오류 페이지 등에도 보안 헤더 적용
- 일관된 보안 정책 유지를 위해 권장
권장 사항
- 환경별 설정 분리
- 개발/스테이징/프로덕션 환경별로 다른 설정 사용
- 환경변수나 include 파일 활용
- SSL/TLS 설정 추가
- HTTPS 사용 강제
- 최신 TLS 버전 사용
- 강력한 암호화 스위트 설정
- 로깅 설정 추가
- 접근 로그
- 오류 로그
- 로그 포맷 커스터마이징
- 성능 최적화
- Gzip 압축 활성화
- 브라우저 캐싱 설정
- 버퍼 크기 최적화
- 주기적인 설정 검토
- 보안 취약점 점검
- 최신 보안 권장사항 적용
- 불필요한 설정 제거
댓글남기기