NGINX의 트레일링 슬래시 및 논 트레일링 슬래시

작성자

카테고리:

← 피드로
DEV Community · Rohit Sharma · 2026-06-23 개발(SW)
Cover image for Trailing and Non Trailing Slash in Nginx

Rohit Sharma

Most tricky part of Nginx: The trailing slash behavior in proxy_pass is one of the most confusing parts of Nginx. Once you understand one rule, it becomes easy

There are 2 Cases:

CASE 1: proxy_pass WITHOUT trailing slash

location /api/ {
    proxy_pass http://localhost:3000;
}

Enter fullscreen mode Exit fullscreen mode

notice

http://localhost:3000
                     ↑
                 no slash

Enter fullscreen mode Exit fullscreen mode

request

http://localhost/api/users

Enter fullscreen mode Exit fullscreen mode

What Nginx sends to backend

http://localhost:3000/api/users

Enter fullscreen mode Exit fullscreen mode

The entire original URI is preserved.

CASE 2: proxy_pass WITH trailing slash

location /api/ {
    proxy_pass http://localhost:3000/;
}

Enter fullscreen mode Exit fullscreen mode

notice

http://localhost:3000/
                     ↑
                  slash

Enter fullscreen mode Exit fullscreen mode

request

http://localhost/api/users

Enter fullscreen mode Exit fullscreen mode

what backend receives

http://localhost:3000/users

Enter fullscreen mode Exit fullscreen mode

Nginx removes the matched location (/api/) and replaces it with /

VISUAL REPRESENTATION (Without Slash)

Client:
    /api/users

location:
    /api/

proxy_pass:
    http://localhost:3000

Result:
    http://localhost:3000/api/users

Enter fullscreen mode Exit fullscreen mode

VISUAL REPRESENTATION (With Slash)

Client:
    /api/users

location:
    /api/

proxy_pass:
    http://localhost:3000/

Result:
    http://localhost:3000/users

Enter fullscreen mode Exit fullscreen mode

Why? Because internally Nginx does:

NO SLASH

backend_url + original_uri

http://localhost:3000
+
/api/users

=
http://localhost:3000/api/users

Enter fullscreen mode Exit fullscreen mode

WITH SLASH

replace(location_prefix, proxy_pass_uri)

/api/users
↓ remove /api/
/users

prepend /
↓

http://localhost:3000/users

Enter fullscreen mode Exit fullscreen mode

In simpler words, think of it as:

No slash (Keep the original URI)
Slash (Replace the matched prefix)

Nginx does NOT look at whether /api/ exists in the request and remove it automatically.

It only removes the matched location prefix when proxy_pass contains a URI part (a trailing slash or some path).

Understanding the both cases carefully
Case 1: No trailing slash

location /api/ {
    proxy_pass http://localhost:3000;
}

Enter fullscreen mode Exit fullscreen mode

notice

proxy_pass = http://localhost:3000

Enter fullscreen mode Exit fullscreen mode

There is:

scheme → http
host → localhost
port → 3000
URI part → ❌ NONE

Enter fullscreen mode Exit fullscreen mode

Because there is no URI part, Nginx simply forwards the entire original URI.

Request : /api/users

Nginx does

http://localhost:3000
+
/api/users
=
http://localhost:3000/api/users

Enter fullscreen mode Exit fullscreen mode

No replacement happens.

CASE 2: TRAILING SLASH

location /api/ {
    proxy_pass http://localhost:3000/;
}

Enter fullscreen mode Exit fullscreen mode

notice

proxy_pass = http://localhost:3000/

Enter fullscreen mode Exit fullscreen mode

There is / at end which is a URI part

/

Enter fullscreen mode Exit fullscreen mode

As there is / in the URI part

scheme → http
host → localhost
port → 3000
URI part → /

Enter fullscreen mode Exit fullscreen mode

Now Nginx switches to replacement mode.

Request: /api/users
Matched location: /api/
Remaining: users
Result: http://localhost:3000/users

Enter fullscreen mode Exit fullscreen mode

Why does / count as a URI?

Because internally Nginx parses:

http://localhost:3000

as:

host = localhost
port = 3000
uri = NULL

Enter fullscreen mode Exit fullscreen mode

but

http://localhost:3000/

as:

host = localhost
port = 3000
uri = /

Enter fullscreen mode Exit fullscreen mode

That tiny / changes the algorithm completely.

Internal logic (simplified). Nginx does something like:

if (proxy_pass_contains_uri) {
    replace_location_prefix();
}
else {
    append_original_uri();
}

Enter fullscreen mode Exit fullscreen mode

OR IN SOME SIMPLE WORDS, How Nginx actually thinks

Did proxy_pass contain a URI part?
            |
      +-----+-----+
      |           |
     NO          YES
      |           |
Keep URI      Replace matched location prefix
as-is         with proxy_pass URI

Enter fullscreen mode Exit fullscreen mode

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다