Web Server Advanced

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.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

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., /dashboard instead 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.css instead of /app/css/style.css, or the backend receives /subfolderstyle.css due 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:

  1. proxy_pass URL includes a URI path component (e.g., http://backend/subfolder/ or http://backend/subfolder):

    • In this scenario, Nginx performs a URI replacement. The part of the incoming request URI that matches the location directive's path is replaced by the URI path component specified in proxy_pass. The presence or absence of a trailing slash in both location and proxy_pass dictates the exact replacement.

    • Common Error Scenario 1: Expecting /app/foo to map to backend/subfolder/foo, but configuring:

      location /app/ {            # Has trailing slash
          proxy_pass http://backend/subfolder; # NO trailing slash on proxy_pass path
      }
      

      Request for /app/foo results in Nginx sending http://backend/subfolderfoo to the upstream. The /app/ is stripped, and foo is appended to subfolder. This is almost always incorrect.

    • Common Error Scenario 2: Expecting /app/foo to map to backend/subfolder/foo, but configuring:

      location /app {             # NO trailing slash
          proxy_pass http://backend/subfolder/; # Has trailing slash
      }
      

      Request for /app/foo results in Nginx sending http://backend/subfolder//foo or http://backend/subfolder/app/foo (depending on Nginx version and exact location match type). The entire location match (/app) is replaced by /subfolder/. The original /app/foo becomes /subfolder//foo, potentially causing 404s or redirects due to double slashes or incorrect path segments.

  2. proxy_pass URL does not include a URI path component (e.g., http://backend or http://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/foo results in Nginx sending http://backend/app/foo to the upstream. This can be desired if the backend application expects the full path including the /app/ prefix.

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 location block path ends with a trailing slash (e.g., location /app/), and your proxy_pass target URL also ends with a trailing slash (e.g., http://your_backend_ip_or_domain/subfolder/), Nginx will strip the location path from the request URI and append the remainder to the proxy_pass URL exactly.
    • Example:
      location /app/ {
          proxy_pass http://your_backend_ip_or_domain/subfolder/;
      }
      
      A request to https://yourdomain.com/app/resource.html will be sent to http://your_backend_ip_or_domain/subfolder/resource.html. This is the recommended configuration for subfolder proxying.

When proxy_pass contains a URI, Nginx uses a replacement mechanism. The part of the URI matching the location is replaced by the URI in proxy_pass. The trailing slash dictates what part is matched and replaced. If both location and proxy_pass have trailing slashes, it's a direct prefix-to-prefix replacement. If proxy_pass lacks 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/.

  1. List available configurations:
    ls -l /etc/nginx/sites-available/
    
  2. Open the relevant configuration file (e.g., your_domain.conf):
    sudo nano /etc/nginx/sites-available/your_domain.conf
    
    or
    sudo 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/resource will correctly map to http://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 rewrite directives can be powerful but also difficult to debug. Ensure your regular expressions are precise and thoroughly tested. When using rewrite before proxy_pass, it's generally best for the proxy_pass directive not to contain a URI path itself (e.g., http://backend-server:8080), as the URI has already been transformed by rewrite.

4. Test Nginx Configuration and Reload

After making any changes to your Nginx configuration, always test the syntax for errors before reloading:

  1. Test Nginx configuration syntax:

    sudo nginx -t
    

    If you see test is successful, proceed. If there are errors, review the output carefully to pinpoint the line causing the issue.

  2. Reload Nginx to apply changes:

    sudo systemctl reload nginx
    

    If nginx -t was successful, reload should also succeed without issues. If you encounter problems, sudo systemctl status nginx might offer clues.

5. Verify Resolution

  1. Clear Browser Cache: Nginx and application-level redirects or caching can sometimes mask changes. Clear your browser's cache before retesting.
  2. Access the Application: Open your web browser and navigate to the proxied subfolder path (e.g., https://yourdomain.com/app/).
  3. 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.
  4. 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:
    sudo tail -f /var/log/nginx/access.log
    sudo tail -f /var/log/nginx/error.log
    
    You should see 200 OK responses from the backend for the intended paths, indicating successful resolution.
👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

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.