How to Prevent Nginx from Auto-Adding Trailing Slash in Location Redirects


2 views

When working with Nginx proxy configurations, you might encounter an unexpected 301 redirect when accessing URLs without trailing slashes. Consider this common scenario:

location /pass/ {
    proxy_pass http://localhost:12345/;
}

While this configuration correctly proxies requests like http://example.com/pass/whatever to http://localhost:12345/whatever, it automatically redirects http://example.com/pass to http://example.com/pass/ with a 301 status.

Nginx treats locations ending with slashes as directory requests and enforces the trailing slash for "directory" locations. This behavior is baked into Nginx's core functionality for proper directory request handling.

Solution 1: Exact Match Location Block

Add an exact match location for the path without slash:

location = /pass {
    # Your custom handling for /pass without slash
    return 404; # or proxy_pass to another location
}

location /pass/ {
    proxy_pass http://localhost:12345/;
}

Solution 2: Rewrite Before Processing

Use a rewrite rule to handle the non-slash version differently:

location /pass {
    rewrite ^/pass$ /alternative-path last;
    rewrite ^ /pass/ break;
    proxy_pass http://localhost:12345/;
}

Solution 3: Disable Directory Slash (Not Recommended)

While possible, disabling this globally isn't recommended as it may affect other locations:

server {
    disable_symlinks off;
    server_name example.com;
    
    location /pass {
        proxy_pass http://localhost:12345/;
    }
}

The cleanest solution is to use exact match locations combined with your regular proxy pass:

# Handle exact /pass (no slash)
location = /pass {
    # Custom logic here
    add_header X-No-Redirect "true";
    proxy_pass http://localhost:12345;
}

# Handle /pass/ and subpaths
location /pass/ {
    proxy_pass http://localhost:12345/;
}

After making changes, always test with:

nginx -t
systemctl reload nginx

Then verify with curl:

curl -I http://example.com/pass
curl -I http://example.com/pass/

Many developers encounter this common Nginx behavior where requests to a directory-like location without a trailing slash get automatically redirected with a 301 status code to the same URL but with a trailing slash appended. While this might be desirable in some cases, it can cause problems when you specifically want to handle the non-trailing-slash version differently.

Nginx treats locations ending with a slash as directory requests by default. When it sees a request to /pass (without slash) that matches a location block defined as /pass/, it assumes you meant to access the directory and "helpfully" redirects you.

Here's the typical problematic configuration:

location /pass/ {
    proxy_pass http://localhost:12345/;
}

The most straightforward solution is to use an exact match location for the non-slash version:

location = /pass {
    # Your handling for non-slash version here
    return 404; # Example: treat as not found
    # or proxy_pass to another backend
}

location /pass/ {
    proxy_pass http://localhost:12345/;
}

If you want to internally rewrite the non-slash version to the slash version without a redirect:

location /pass {
    rewrite ^/pass$ /pass/ last;
}

location /pass/ {
    proxy_pass http://localhost:12345/;
}

While possible, this approach might have unintended consequences:

server {
    ...
    disable_symlinks off;
    server_name_in_redirect off;
    port_in_redirect off;
    ...
}

After making changes, always test with:

nginx -t
service nginx reload

Then verify the behavior with curl:

curl -I http://example.com/pass
curl -I http://example.com/pass/