Nginx proxy_pass Trailing Slash Subfolder Resolution Error on Ubuntu 20.04 LTS
Fix Nginx proxy_pass misconfigurations causing subfolder resolution issues due to trailing slashes on Ubuntu 20.04 LTS. Optimize your reverse proxy setup.
Fix Nginx proxy_pass misconfigurations causing subfolder resolution issues due to trailing slashes on Ubuntu 20.04 LTS. Optimize your reverse proxy setup.
When configuring Nginx as a reverse proxy, mapping a public subfolder path (e.g., example.com/app/) to a backend application running on a different subfolder path (e.g., http://backend-server/subfolder/) can lead to frustrating resolution errors. These issues often manifest as 404s, incorrect content being served, or broken asset links, all stemming from subtle differences in how Nginx's proxy_pass directive handles trailing slashes in both the location block and the target URL. This guide will clarify Nginx's URI rewriting logic and provide precise steps to resolve these common subfolder proxying errors on Ubuntu 20.04 LTS.
Symptom & Error Signature
Users attempting to access your proxied application via a subfolder (e.g., https://yourdomain.com/app/dashboard) might encounter:
- 404 Not Found errors: Either from Nginx itself or, more commonly, from the backend application because the URI passed upstream is incorrect (e.g.,
/dashboardinstead of/subfolder/dashboard). - Broken UI/UX: The application loads, but CSS, JavaScript, images, or API calls fail due to incorrect relative paths (e.g., the browser tries to fetch
/css/style.cssinstead of/app/css/style.css, or the backend receives/subfolderstyle.cssdue to incorrect URI rewriting). - Unexpected redirects: The backend application might issue a redirect to an incorrect path, or Nginx might issue a redirect due to URI normalization.
Typical entries in Nginx access.log might show requests hitting the location block but being mapped incorrectly upstream, leading to a backend 404:
# Example Nginx access.log entry showing a 404 from the backend
# Request to /app/index.html results in backend responding with 404
192.168.1.10 - - [03/Oct/2026:10:30:00 +0000] "GET /app/index.html HTTP/1.1" 404 153 "-" "Mozilla/5.0 (...)"
And if the Nginx error.log is verbose enough, you might see clues about the URI being passed:
# Nginx error.log (often not verbose enough for URI-rewriting issues by default)
2026/10/03 10:30:00 [error] 12345#12345: *6789 upstream sent no valid HTTP/1.0 header while reading response header from upstream, client: 192.168.1.10, server: yourdomain.com, request: "GET /app/index.html HTTP/1.1", upstream: "http://your_backend_ip:8080/index.html", host: "yourdomain.com"
# Note: The 'upstream' URI might reveal the incorrect path
The key symptom is that the URI reaching the backend server is not what the backend expects for the subfolder application.
Root Cause Analysis
The root cause of "Nginx proxy_pass trailing slash subfolder resolving errors" lies in Nginx's subtle but powerful URI transformation rules when using the proxy_pass directive. The behavior depends critically on whether a trailing slash exists at the end of the location path and at the end of the proxy_pass URL.
Nginx applies different logic based on the structure of the proxy_pass directive:
proxy_passURL includes a URI path component (e.g.,http://backend/subfolder/orhttp://backend/subfolder):In this scenario, Nginx performs a URI replacement. The part of the incoming request URI that matches the
locationdirective's path is replaced by the URI path component specified inproxy_pass. The presence or absence of a trailing slash in bothlocationandproxy_passdictates the exact replacement.Common Error Scenario 1: Expecting
/app/footo map tobackend/subfolder/foo, but configuring:location /app/ { # Has trailing slash proxy_pass http://backend/subfolder; # NO trailing slash on proxy_pass path }Request for
/app/fooresults in Nginx sendinghttp://backend/subfolderfooto the upstream. The/app/is stripped, andfoois appended tosubfolder. This is almost always incorrect.Common Error Scenario 2: Expecting
/app/footo map tobackend/subfolder/foo, but configuring:location /app { # NO trailing slash proxy_pass http://backend/subfolder/; # Has trailing slash }Request for
/app/fooresults in Nginx sendinghttp://backend/subfolder//fooorhttp://backend/subfolder/app/foo(depending on Nginx version and exactlocationmatch type). The entirelocationmatch (/app) is replaced by/subfolder/. The original/app/foobecomes/subfolder//foo, potentially causing 404s or redirects due to double slashes or incorrect path segments.
proxy_passURL does not include a URI path component (e.g.,http://backendorhttp://backend:8080):- In this simpler scenario, Nginx passes the entire incoming request URI, unmodified, to the upstream server.
- Example:
location /app/ { proxy_pass http://backend; }- Request for
/app/fooresults in Nginx sendinghttp://backend/app/footo the upstream. This can be desired if the backend application expects the full path including the/app/prefix.
- Request for
The "trailing slash subfolder resolving error" specifically targets the misapplication of Rule 1, where the Nginx URI replacement logic leads to an unintended backend path. The most common desire is to map example.com/app/foo to backend/subfolder/foo, which requires careful handling of trailing slashes.
Step-by-Step Resolution
1. Understand Nginx proxy_pass Trailing Slash Semantics
The most robust and predictable way to proxy requests to a backend subfolder is to ensure consistency in trailing slashes when you intend to map one path prefix to another.
- Rule: If your
locationblock path ends with a trailing slash (e.g.,location /app/), and yourproxy_passtarget URL also ends with a trailing slash (e.g.,http://your_backend_ip_or_domain/subfolder/), Nginx will strip thelocationpath from the request URI and append the remainder to theproxy_passURL exactly.- Example:
A request tolocation /app/ { proxy_pass http://your_backend_ip_or_domain/subfolder/; }https://yourdomain.com/app/resource.htmlwill be sent tohttp://your_backend_ip_or_domain/subfolder/resource.html. This is the recommended configuration for subfolder proxying.
- Example:
When
proxy_passcontains a URI, Nginx uses a replacement mechanism. The part of the URI matching thelocationis replaced by the URI inproxy_pass. The trailing slash dictates what part is matched and replaced. If bothlocationandproxy_passhave trailing slashes, it's a direct prefix-to-prefix replacement. Ifproxy_passlacks a trailing slash, it appends the remainder directly, often leading to concatenated paths like/subfolderfoo.
2. Identify and Edit Nginx Configuration
Locate the Nginx configuration file responsible for your domain or application. On Ubuntu, these are typically found in /etc/nginx/sites-available/.
- List available configurations:
ls -l /etc/nginx/sites-available/ - Open the relevant configuration file (e.g.,
your_domain.conf):
orsudo nano /etc/nginx/sites-available/your_domain.confsudo vim /etc/nginx/sites-available/your_domain.conf
3. Correct location and proxy_pass Directives
Modify the location block to ensure the correct proxy_pass behavior.
Scenario: Proxying yourdomain.com/app/ to http://backend-server/subfolder/
This is the most common and recommended approach for mapping a public subfolder path to a different backend subfolder path.
# Correct configuration for /app/ -> http://backend-server/subfolder/
location /app/ {
# Ensure BOTH location path and proxy_pass URL END with a trailing slash.
proxy_pass http://your_backend_ip_or_domain:8080/subfolder/;
# Standard proxy headers for preserving client IP, host, and protocol
proxy_set_header Host $host;
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;
# Optional: Adjust timeouts if your backend is slow
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout 60s;
# Optional: Increase buffer sizes if upstream sends large responses
# proxy_buffers 8 16k;
# proxy_buffer_size 32k;
}
In this configuration, a request for
https://yourdomain.com/app/resourcewill correctly map tohttp://your_backend_ip_or_domain:8080/subfolder/resource. The/app/prefix is effectively replaced by/subfolder/.
Alternative Scenario: Using rewrite for More Complex URI Transformations
If your mapping logic is more complex or you need very specific URI manipulation that proxy_pass alone can't achieve intuitively, the rewrite directive can be used. This allows you to explicitly transform the URI before it's passed to proxy_pass.
# Alternative: Using rewrite to achieve /app/ -> http://backend-server/subfolder/
# This is often used when the backend expects a root path (e.g., / ) for the app,
# but Nginx needs to handle the /app/ prefix.
location ~ ^/app/(.*)$ { # Matches /app/ and captures everything after it
# Rewrite the URI to remove '/app/' and prepend '/subfolder/'
rewrite ^/app/(.*)$ /subfolder/$1 break; # 'break' stops further rewrite processing
proxy_pass http://your_backend_ip_or_domain:8080; # Note: NO path in proxy_pass
proxy_set_header Host $host;
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;
}
Using
rewritedirectives can be powerful but also difficult to debug. Ensure your regular expressions are precise and thoroughly tested. When usingrewritebeforeproxy_pass, it's generally best for theproxy_passdirective not to contain a URI path itself (e.g.,http://backend-server:8080), as the URI has already been transformed byrewrite.
4. Test Nginx Configuration and Reload
After making any changes to your Nginx configuration, always test the syntax for errors before reloading:
Test Nginx configuration syntax:
sudo nginx -tIf you see
test is successful, proceed. If there are errors, review the output carefully to pinpoint the line causing the issue.Reload Nginx to apply changes:
sudo systemctl reload nginxIf
nginx -twas successful,reloadshould also succeed without issues. If you encounter problems,sudo systemctl status nginxmight offer clues.
5. Verify Resolution
- Clear Browser Cache: Nginx and application-level redirects or caching can sometimes mask changes. Clear your browser's cache before retesting.
- Access the Application: Open your web browser and navigate to the proxied subfolder path (e.g.,
https://yourdomain.com/app/). - Check Developer Tools:
- Open your browser's developer tools (usually F12).
- Go to the "Network" tab.
- Hard refresh the page (Ctrl+F5 or Cmd+Shift+R).
- Inspect the requests:
- Are static assets (CSS, JS, images) loading correctly from the
/app/path? - Are API calls being made to the correct
/app/api/endpoints? - Look for any 404s, 500s, or unexpected redirects.
- Are static assets (CSS, JS, images) loading correctly from the
- Monitor Nginx Logs:
Keep an eye on Nginx access and error logs to confirm that requests are now being forwarded to the backend with the correct URI paths:
You should see 200 OK responses from the backend for the intended paths, indicating successful resolution.sudo tail -f /var/log/nginx/access.log sudo tail -f /var/log/nginx/error.log
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.