이전 글에서는 Virtual Host를 통해 하나의 Nginx에서 여러 도메인을 운영하는 방법을 학습하였다.
이번에는 클라이언트의 요청 URI를 변경하는 두 가지 방법인 return(리다이렉트) 과 rewrite 에 대해 파악하고 정리해보겠다.
웹 서비스를 운영하다보면, 다양한 이유로 인해서 URL을 변경하는 경우가 있을 것이다.
예를 들어, 사이트 구조를 변경/개편하거나 HTTP를 HTTPS로 강제하거나 오래된 URL을 새로운 도메인으로 변경하는 등 이유가 있을 것이다.
Rewrite vs Redirect
먼저 rewrite와 redirect의 차이에 대해서 살펴보자.
두 방식은 URI를 변경한다는 목적은 같지만, 동작 방식이 근본적으로 다르다.
Redirect
리다이랙트는 클라이언트에게 ' 다른 URL로 다시 요청하라 '고 응답한다. 따라서 브라우저 주소창의 URL이 변경된다.
이는 즉 서버에서 다시 클라이언트로 응답을 하면, 클라이언트가 다시 해당 URL에 맞는 주소로 요청을 보내는 네트워크 과정이 발생한다.
아래와 같은 예시를 통해 이해해보자.


이처럼 Redirect는 같은 서버 내 존재하는 다른 리소스(jsp등) 불러오거나 다른 서비스에 있는 리소스를 불러올 수 있다.
즉 네트워크의 흐름을 보자면 아래와 같은 과정이 이뤄지는 것이다.

이를 실습하기 위해서, nginx.conf 파일을 아래와 같이 구성해보자.

위의 설정은 old-page로 요청이 온다면, 301 응답과 함께 new-page로 가도록 명시하는 설정이다.
301 코드는 요청한 리소스가 영구적으로 다른 url로 이동했다는 의미를 가지고 있다.
따라서 해당 페이지(old-page)는 더 이상 사용되지 않고, new-page 로 이동해라고 응답을 주는 것이다.
이에 클라이언트는 new-page로 다시 요청한다.
그리고 이러한 상태코드에는 다음과 같은 항목들이 존재한다.
Http 리다이랙트 코드값의 종류

| 코드값 | 코드값 설명 및 예시 |
| 302- Found ( 임시 이동 ) | 현재 점검 중으로 다른 URL로 이동, 나중에 점검이 끝나고 다시 URL 요청 가 |
| 307,308 ( HTTP 메서드 유지) | 301또는 302는 post를 get 요청으로 변경 가능한 반면, 307과 308은 post면 post로 redirect하는 코드값에 해당 |
| 303 ( Post를 Get으로 리다이랙트 ) | Post 요청을 Get으로 리다이렉트 |
Redirect에서 return에 변수 사용 방법
지금까지의 과정처럼 redirect할 때, return하는 구문 사용이 가능한데, 여기서 변수도 선언하여 사용이 가능하다.
예를 들어 아래와 같은 설정이다.

먼저 $host는 요청의 host 헤더값에 해당하며, $request_uri는 쿼리스트링을 포함한 전체 URI다.
이는 http://example.com/page?id=1 로 요청이 오면, https://example.com/page?id=1 으로 리다이랙트 한다.
즉 두 변수를 조합하면 경로와 파라미터를 그대로 유지한 채 HTTPS로 리다이랙트가 가능한 것이다.
Rewrite
한편, Rewrite는 redirect와 다르게 요청을 다른 URI로 재요청 하는 것이 아닌 내부적으로 다른 URI로 변환하는 것이다.
Spring을 공부했다면 RequestDispatcher.forward()와 비교하면 이해가 빠르다.

사용에 있어서는 uri 매칭에 있어, 주로 정규식으로 URI를 매칭하여 사용한다.
rewrite의 문법은 다음과 같다.
rewrite [정규식] [바꿀 URI] [플래그];
여기에서 rewrite 플래그로 아래와 같은 설정 문법들이 있고, 이들을 조합하여 사용 가능하다.

그리고 이들 중 주로 사용되는 플래그는 'last'와 'break'이며,
예를들어 아래와 같은 예시를 볼 수 있다.
location /download/ {
rewrite ^/download/(.*)$ /files/$1 last;
# /download/image.png → /files/image.png
# 이후 location /files/ 블록으로 다시 매칭
}
location /files/ {
return 200 "Files location: $uri\n";
}
이를 단계적으로 살펴보자.
rewrite ^/download/(.*)$ /files/$1 last;
rewrite → 디렉티브 이름
^/download/(.*)$ → 현재 URI가 이 정규식에 매칭되면
/files/$1 → 이 URI로 바꿔라
last → 바꾼 뒤 이렇게 처리해라 (플래그)
정규식 부분을 더 자세히 분해하면 다음과 같다.
^/download/(.*)$
^ → URI의 시작
/download/ → 이 문자열로 시작하고
(.*) → 그 뒤에 오는 모든 문자를 캡처 (괄호 = 캡처 그룹)
$ → URI의 끝
last vs break
주로 사용되는 플래그인 last와 break의 차이에 대해서 살펴보자.
last — URI를 바꾼 뒤 변경된 URI로 location 블록을 처음부터 다시 매칭한다.
location /download/ {
rewrite ^/download/(.*)$ /files/$1 last;
# ↑ 정규식 매칭 ↑ 새 URI ↑ 플래그
#
# ^/download/(.*)$ 가 /download/image.png 에 매칭
# (.*) 에 "image.png" 캡처 → $1 = "image.png"
# /files/$1 = /files/image.png 로 URI 변환
}
location /files/ {
return 200 "Files location: $uri\n";
}
break — URI를 바꾼 뒤 새로운 location 매칭 없이 현재 location에서 계속 처리한다.
location /download/ {
rewrite ^/download(.*)$ /files/$1 break;
# ↑ 정규식 매칭 ↑ 새 URI ↑ 플래그
#
# ^/download(.*)$ 가 /download/image.png 에 매칭
# (.*) 에 "/image.png" 캡처 → $1 = "/image.png"
# /files/$1 = /files/image.png 로 URI 변환
root /var/www;
}
두 플래그의 핵심 차이는 location 블록을 넘어가냐 안 넘어가냐다.
last vs break — 실제 영향과 사용 시점 및 영향
last 는 location 블록을 넘어가기 때문에, 변환된 URI가 매칭되는 다른 location 블록의 모든 설정이 적용된다.
URI를 바꾼 뒤 다른 location 블록의 로직(인증, 캐시, 프록시 등)을 태우고 싶을 때 사용할 수 있다.
# API 버전 변환 — /api/v1/ 요청을 /api/v2/ 로 바꾸고
# location /api/v2/ 블록의 인증, 로깅 등을 적용하고 싶을 때
location /api/v1/ {
rewrite ^/api/v1/(.*)$ /api/v2/$1 last;
}
location /api/v2/ {
# 여기서 인증, 로깅 등 처리
proxy_pass http://backend;
}
# 블로그 URL 변환 — /blog/123 → /posts?id=123
# location /posts 블록에서 처리하고 싶을 때
location /blog/ {
rewrite ^/blog/([0-9]+)$ /posts?id=$1 last;
}
location /posts {
proxy_pass http://backend;
}
반면, break 는 location 블록을 넘어가지 않기 때문에, 현재 location 블록에서 바로 파일을 서빙할 때 사용될 수 있다.
# /download/ 요청을 받아서 URI만 /files/ 로 바꾸고
# 현재 블록에서 바로 파일 서빙
location /download/ {
rewrite ^/download/(.*)$ /files/$1 break;
root /var/www;
# 최종 경로: /var/www/files/image.png 바로 서빙
}
또한 무한 루프가 발생할 수 있는 상황에서도 break를 사용해야 한다.
# ❌ last를 쓰면 무한 루프 발생
location /files/ {
rewrite ^/files/(.*)$ /files/$1 last;
# URI가 /files/... 인데 last로 재매칭하면
# 또 location /files/ 로 들어와서 무한 반복!
}
# ✅ break를 쓰면 안전
location /files/ {
rewrite ^/files/(.*)$ /files/$1 break;
# URI 바꾸고 재매칭 없이 현재 블록에서 처리
root /var/www;
}
return vs rewrite 선택 기준
어떤 상황에서 무엇을 쓸지 헷갈릴 때는 아래 순서로 판단하면 된다.
- 정규식이 필요한가? → Yes: rewrite 사용 → No: 아래로
- 단순 리다이렉트인가? → Yes: return 사용 (권장) → No: 아래로
- 내부 URI 변환인가? → Yes: rewrite 사용 → No: return 사용 (권장)

단순히 URL을 바꿔서 클라이언트에게 알려주는 경우라면 return이 더 간결하다.
rewrite는 정규식 처리 비용이 있으므로 내부 URI 변환이 필요한 경우에만 사용하는 것이 권장된다.
개인적으로, 유지보수 관리 측면에서 가능하다면 복잡한 rewrite보단 redirect를 사용하는 것이 더 좋아보인다.
그리고 현실적으로 대부분 정규식이 꼭 필요한 것이 아니라면, 대부분 리턴 처리만으로도 해결 가능할 것 같다.
'Nginx' 카테고리의 다른 글
| HTTP1.0/HTTP1.1을 기반으로 하는 다이나믹 리버스 프록시 (0) | 2026.07.08 |
|---|---|
| 다중 도메인 호스팅을 위한 Virtual Host ( Server Block ) (0) | 2026.06.09 |
| 정적 파일 서빙 Path Matching 기법 - root와 alias (0) | 2026.05.28 |
| 네트워크 전송 관점에서 최적화를 위한 압축 설정 템플릿 (0) | 2026.05.21 |
| Nginx 성능 최적화 설정과 기본 템플릿 (0) | 2026.05.16 |
댓글