{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreig5ptrnyymwrwson5lckqzl4c3mhtwhsmkbzdfwft3q6jijg27iym",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mowvpavlhob2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreigv4hnvzb5hwhraxp6htu5arffjmzovzbgtsrrwdkk7fcp6mo36vm"
},
"mimeType": "image/webp",
"size": 368032
},
"path": "/rohitsharmaj7/trailing-and-non-trailing-slash-in-nginx-1ghp",
"publishedAt": "2026-06-23T07:22:42.000Z",
"site": "https://dev.to",
"tags": [
"backend",
"devops",
"tutorial",
"webdev"
],
"textContent": "_**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_\n\n**There are 2 Cases:**\n\n**CASE 1: proxy_pass WITHOUT trailing slash**\n\n\n\n location /api/ {\n proxy_pass http://localhost:3000;\n }\n\n\nnotice\n\n\n\n http://localhost:3000\n ↑\n no slash\n\n\nrequest\n\n\n\n http://localhost/api/users\n\n\nWhat Nginx sends to backend\n\n\n\n http://localhost:3000/api/users\n\n\n**The entire original URI is preserved.**\n\n**CASE 2: proxy_pass WITH trailing slash**\n\n\n\n location /api/ {\n proxy_pass http://localhost:3000/;\n }\n\n\nnotice\n\n\n\n http://localhost:3000/\n ↑\n slash\n\n\nrequest\n\n\n\n http://localhost/api/users\n\n\nwhat backend receives\n\n\n\n http://localhost:3000/users\n\n\n**Nginx removes the matched location (/api/) and replaces it with /**\n\n**VISUAL REPRESENTATION (Without Slash)**\n\n\n\n Client:\n /api/users\n\n location:\n /api/\n\n proxy_pass:\n http://localhost:3000\n\n Result:\n http://localhost:3000/api/users\n\n\n**VISUAL REPRESENTATION (With Slash)**\n\n\n\n Client:\n /api/users\n\n location:\n /api/\n\n proxy_pass:\n http://localhost:3000/\n\n Result:\n http://localhost:3000/users\n\n\n**Why? Because internally Nginx does:**\n\nNO SLASH\n\n\n\n backend_url + original_uri\n\n http://localhost:3000\n +\n /api/users\n\n =\n http://localhost:3000/api/users\n\n\nWITH SLASH\n\n\n\n replace(location_prefix, proxy_pass_uri)\n\n /api/users\n ↓ remove /api/\n /users\n\n prepend /\n ↓\n\n http://localhost:3000/users\n\n\n**In simpler words, think of it as:**\n\nNo slash (Keep the original URI)\nSlash (Replace the matched prefix)\n\nNginx does NOT look at whether /api/ exists in the request and remove it automatically.\n\nIt only removes the matched location prefix when proxy_pass contains a URI part (a trailing slash or some path).\n\nUnderstanding the both cases carefully\n**Case 1: No trailing slash**\n\n\n\n location /api/ {\n proxy_pass http://localhost:3000;\n }\n\n\nnotice\n\n\n\n proxy_pass = http://localhost:3000\n\n\nThere is:\n\n\n\n scheme → http\n host → localhost\n port → 3000\n URI part → ❌ NONE\n\n\nBecause there is no URI part, Nginx simply forwards the entire original URI.\n\n`Request : /api/users`\n\nNginx does\n\n\n\n http://localhost:3000\n +\n /api/users\n =\n http://localhost:3000/api/users\n\n\n**No replacement happens.**\n\n**CASE 2: TRAILING SLASH**\n\n\n\n location /api/ {\n proxy_pass http://localhost:3000/;\n }\n\n\nnotice\n\n\n\n proxy_pass = http://localhost:3000/\n\n\nThere is / at end which is a URI part\n\n\n\n /\n\n\nAs there is / in the URI part\n\n\n\n scheme → http\n host → localhost\n port → 3000\n URI part → /\n\n\n**Now Nginx switches to replacement mode.**\n\n\n\n Request: /api/users\n Matched location: /api/\n Remaining: users\n Result: http://localhost:3000/users\n\n\nWhy does / count as a URI?\n\nBecause internally Nginx parses:\n\n`http://localhost:3000`\n\nas:\n\n\n\n host = localhost\n port = 3000\n uri = NULL\n\n\n\nbut\n\n`http://localhost:3000/`\n\nas:\n\n\n\n host = localhost\n port = 3000\n uri = /\n\n\n**That tiny / changes the algorithm completely.**\n\n**Internal logic (simplified). Nginx does something like:**\n\n\n\n if (proxy_pass_contains_uri) {\n replace_location_prefix();\n }\n else {\n append_original_uri();\n }\n\n\nOR IN SOME SIMPLE WORDS, How Nginx actually thinks\n\n\n\n Did proxy_pass contain a URI part?\n |\n +-----+-----+\n | |\n NO YES\n | |\n Keep URI Replace matched location prefix\n as-is with proxy_pass URI\n",
"title": "Trailing and Non Trailing Slash in Nginx"
}