Nginx proxy_pass Trailing Slash Subfolder Resolution Error on Windows WSL2 Ubuntu
Resolve Nginx proxy_pass 404s in WSL2 Ubuntu subfolder setups. Understand trailing slash behavior to correctly route requests to backend applications.
Resolve Nginx proxy_pass 404s in WSL2 Ubuntu subfolder setups. Understand trailing slash behavior to correctly route requests to backend applications.
When developing or hosting applications on Windows using WSL2 (Windows Subsystem for Linux 2) with Nginx as a reverse proxy, you might encounter frustrating 404 Not Found errors or misdirected requests, especially when trying to proxy to a backend application served from a subfolder. This guide delves into a common pitfall: the nuanced behavior of the proxy_pass directive concerning trailing slashes and how it affects URI resolution when targeting subfolders, particularly within the WSL2 Ubuntu environment.
Symptom & Error Signature
Users attempting to access your proxied application via a subfolder path (e.g., https://yourdomain.com/myapp/dashboard) will typically experience one of the following:
Browser: A generic "404 Not Found" page served either by Nginx itself or, more commonly, by the proxied backend application because it received an incorrect URI.
Browser (assets): The main page loads, but static assets (CSS, JS, images) fail to load, resulting in a broken UI. This often means the HTML content was partially proxied, but relative paths for assets are not resolved correctly.
Nginx Access Logs (
/var/log/nginx/access.log): You'll see requests hitting your Nginx server, but thestatuscode from the proxied backend might be404or502(if the backend received a malformed request and crashed or couldn't process it).192.168.1.1 - - [25/Sep/2026:10:30:00 +0000] "GET /myapp/dashboard HTTP/1.1" 404 153 "-" "Mozilla/5.0" 192.168.1.1 - - [25/Sep/2026:10:30:01 +0000] "GET /myapp/static/css/main.css HTTP/1.1" 404 153 "-" "Mozilla/5.0"Nginx Error Logs (
/var/log/nginx/error.log): Depending on the exact misconfiguration, you might see errors indicating connection issues or failures to resolve upstream.2026/09/25 10:30:00 [error] 1234#1234: *5 open() "/etc/nginx/html/myapp/dashboard" failed (2: No such file or directory), client: 192.168.1.1, server: yourdomain.com, request: "GET /myapp/dashboard HTTP/1.1", host: "yourdomain.com" 2026/09/25 10:30:01 [error] 1235#1235: *6 upstream sent no valid HTTP/1.0 header while reading response header from upstream, client: 192.168.1.1, server: yourdomain.com, request: "GET /myapp/dashboard HTTP/1.1", upstream: "http://127.0.0.1:8000/myapp/dashboard"(Note: The second error here implies Nginx tried to pass the full URI
/myapp/dashboardto the backend, which might not be what the backend expects.)Backend Application Logs: The logs of your proxied application might show that it received a request for
/or for/mybackendpathinstead of/mybackendpath/dashboard, leading to a 404 because the specific resource path was dropped.
Root Cause Analysis
The core of this problem lies in a subtle but critical distinction in how Nginx's proxy_pass directive interprets URIs, specifically concerning the presence or absence of a trailing slash in the proxy_pass target path, combined with how it interacts with the location block's URI.
Here's a breakdown of the Nginx proxy_pass logic:
proxy_passto a host/IP without a URI component (e.g.,http://127.0.0.1:8000;): Ifproxy_passonly specifies a host and port (or a host only), Nginx preserves the original request URI (relative to thelocationblock if it's a prefix match) and passes it directly to the upstream server.location /myapp/ { proxy_pass http://127.0.0.1:8000; } # Request: /myapp/dashboard -> Proxied to: http://127.0.0.1:8000/myapp/dashboardThis behavior is often not desired when the backend application expects to be served from its root (
/) rather than/myapp/.proxy_passto a host/IP with a URI component ending in a trailing slash (e.g.,http://127.0.0.1:8000/backend_root/;): Whenproxy_passincludes a URI path component that ends with a trailing slash, Nginx will strip thelocationpath from the original request URI and append the remainder to theproxy_passURI. This is typically the desired behavior for proxying to a subfolder on the backend.location /myapp/ { proxy_pass http://127.0.0.1:8000/backend_root/; # Note the trailing slash here! } # Request: /myapp/dashboard -> Proxied to: http://127.0.0.1:8000/backend_root/dashboardproxy_passto a host/IP with a URI component without a trailing slash (e.g.,http://127.0.0.1:8000/backend_root;): This is the primary source of the "subfolder resolving error." Ifproxy_passincludes a URI path component that does not end with a trailing slash, Nginx will replace the entire matchedlocationpath with the URI specified inproxy_pass. Any subsequent parts of the original request URI are discarded.location /myapp/ { proxy_pass http://127.0.0.1:8000/backend_root; # NO trailing slash here! } # Request: /myapp/dashboard -> Proxied to: http://127.0.0.1:8000/backend_rootIn this problematic scenario, the backend receives a request only for
/backend_root(or/ifbackend_rootis not included), losing the/dashboardpart, hence the 404 or incorrect response.
The WSL2 environment itself does not alter Nginx's core behavior. However, it's a common development setup where developers frequently encounter this specific Nginx proxy_pass nuance when setting up local development proxies for containerized or local applications, leading to confusion when relative paths don't resolve as expected.
Step-by-Step Resolution
To resolve the Nginx proxy_pass trailing slash subfolder resolving error, you need to ensure your proxy_pass directive correctly constructs the URI for your backend application.
1. Understand and Identify the Incorrect proxy_pass Configuration
First, examine your Nginx configuration, typically found in /etc/nginx/sites-available/yourdomain.conf or a similar path within your WSL2 Ubuntu instance. Look for location blocks that use proxy_pass to a subfolder.
Example of problematic configuration:
# /etc/nginx/sites-available/yourdomain.conf
server {
listen 80;
server_name yourdomain.com;
location /myapp/ {
# THIS IS THE PROBLEM: missing trailing slash on /backend_app
proxy_pass http://127.0.0.1:8000/backend_app;
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;
}
}
With this configuration, a request like http://yourdomain.com/myapp/users will be proxied as http://127.0.0.1:8000/backend_app. The /users part of the original URI is discarded, leading to 404s from your backend unless it serves all requests from /backend_app.
2. Apply the Correct Trailing Slash to proxy_pass
This is the most common and direct fix for the described issue. Ensure your proxy_pass directive includes a trailing slash if you intend for the path after the location match to be appended to the proxy_pass URI.
Solution 1: Backend application serves content from a specific subpath (e.g., /backend_app/)
If your backend application (e.g., a Django app mounted at /backend_app, or a Node.js API with all routes prefixed /backend_app/) expects requests to arrive with that subpath, then proxy_pass should include it, with a trailing slash.
# /etc/nginx/sites-available/yourdomain.conf
server {
listen 80;
server_name yourdomain.com;
location /myapp/ {
# CORRECT: Trailing slash on /backend_app/
proxy_pass http://127.0.0.1:8000/backend_app/;
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;
}
}
Now, http://yourdomain.com/myapp/users will be correctly proxied to http://127.0.0.1:8000/backend_app/users. Nginx strips /myapp/ and appends /users to http://127.0.0.1:8000/backend_app/.
Always ensure your backend application is configured to serve content from the path you specify in
proxy_pass. If your backend expects requests to its root (/) but you proxy to/backend_app/, it will still result in 404s.
3. Alternative: Path Rewriting for Different Backend Expectations
Sometimes, your backend application is designed to serve from its root (/), but you want to expose it under a subfolder on your Nginx frontend (e.g., yourdomain.com/myapp/). In this case, you need to strip the /myapp/ prefix before passing the request to the backend. This is achieved using the rewrite directive.
Solution 2: Backend application expects content from its root (/)
# /etc/nginx/sites-available/yourdomain.conf
server {
listen 80;
server_name yourdomain.com;
location /myapp/ {
# Strip the /myapp/ prefix from the URI before proxying
rewrite ^/myapp/(.*)$ /$1 break;
# Proxy to the backend's root
proxy_pass http://127.0.0.1:8000; # No URI component here, passes rewritten URI
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;
}
}
In this setup, http://yourdomain.com/myapp/users is first rewritten to /users by Nginx, and then proxied to http://127.0.0.1:8000/users. This is ideal when your backend doesn't care about the Nginx frontend subfolder.
The
breakflag in therewritedirective stops processing of furtherngx_http_rewrite_moduledirectives within the current location block and moves on to the next phase, which isproxy_passin this case. Withoutbreak, Nginx might continue processing and potentially cause issues or unnecessary redirects.
4. Review and Add Essential proxy_set_header Directives
Always include a standard set of proxy_set_header directives to ensure your backend receives correct information about the client, original host, and protocol. This is crucial for logging, security, and proper application functionality (e.g., generating correct absolute URLs for redirects or assets).
location /myapp/ {
# ... (proxy_pass or rewrite directive) ...
# Essential headers for robust proxying
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Handle WebSocket connections if your application uses them
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 900s; # Adjust as needed for long-running connections
}
5. Test Nginx Configuration and Reload
After making any changes to your Nginx configuration, you must test it for syntax errors and then reload Nginx for the changes to take effect.
Test Nginx Configuration:
sudo nginx -tYou should see
test is successfulif there are no syntax errors. If there are errors, Nginx will output detailed messages indicating the line number and type of error. Correct them before proceeding.Reload Nginx:
sudo systemctl reload nginxThis command reloads the configuration without dropping active connections. If you prefer a full restart (which might briefly drop connections), use
sudo systemctl restart nginx.
Always test your Nginx configuration with
sudo nginx -tbefore reloading or restarting. A faulty configuration can prevent Nginx from starting or reloading, leading to downtime for your websites.
6. Verify and Troubleshoot
After reloading Nginx, immediately test your application in your browser.
- Check application functionality: Ensure all pages load correctly, static assets (CSS, JS, images) appear, and forms/APIs work as expected.
- Monitor Nginx Logs:
tail -f /var/log/nginx/access.logtail -f /var/log/nginx/error.logObserve the HTTP status codes and how the URIs are being requested from the backend.
- Check Backend Application Logs: Monitor your backend application's logs to see what URIs it is receiving and how it responds. This will help confirm if the Nginx proxying is correct from the backend's perspective.
- Use
curl -v: Perform a verbosecurlrequest from your WSL2 environment to simulate client requests and see the full request/response cycle, including any redirects.
Pay attention to thecurl -v http://yourdomain.com/myapp/dashboardLocationheader in redirects and the final resolved URL.
By carefully understanding and applying the rules for proxy_pass with trailing slashes, you can effectively resolve subfolder routing issues in your Nginx reverse proxy setups on Windows WSL2 Ubuntu, ensuring smooth operation for your proxied applications.
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.