Tengine proxy_pass trailing slash trap
2026-09-29
One character difference in proxy_pass silently rewrites your URLs, and it confuses everyone at least once. The rule is simple but easy to forget: a trailing slash in proxy_pass replaces the matching prefix; no trailing slash keeps it.
The rule with examples
# WITHOUT trailing slash: /api/users -> backend receives /api/users
location /api/ {
proxy_pass http://backend:8080;
}
# WITH trailing slash: /api/users -> backend receives /users
location /api/ {
proxy_pass http://backend:8080/;
}
In the second case, the /api/ part of the URI is stripped and replaced by the / after the port. Your backend receives /users instead of /api/users.
Where this bites
- You move a service behind a path prefix (
/api) and suddenly all routes 404 — the backend still expects the full path. - You add a trailing slash thinking it is harmless and the app’s relative links break.
- It is the top cause of “works locally, 404s behind Tengine” reports I see.
How to debug fast
Enable the debug log for one request and watch the rewritten URI:
error_log /usr/local/tengine/logs/error.log debug;
Reload, hit the URL once, then look for the proxy_pass lines showing what path the backend actually received:
grep "proxy" /usr/local/tengine/logs/error.log | tail -20
Summary
Trailing slash in proxy_pass = prefix stripped; no trailing slash = path preserved. Pick one convention and check it before every deploy — this single rule prevents most “phantom 404” tickets. If your proxy is to multiple backends, see my load balancer guide for the full upstream setup.