How to Disable Nginx 301 Redirect for URIs Without Trailing Slashes in Proxy Configurations


8 views

When accessing foobar.com/test, Nginx automatically issues a 301 redirect to foobar.com/test/ before the request reaches your backend Apache server. This happens because:

  1. Nginx's default behavior treats directories differently than files
  2. Your configuration has separate location blocks for both variants
  3. The proxy setup doesn't properly preserve the original URI formatting

The root cause lies in how Nginx processes location matching when acting as a reverse proxy. Unlike direct Apache access where the server handles the URI as-is, Nginx applies normalization rules before proxying.

In your current configuration:

location /test {
    proxy_pass http://1.1.1.1:80/;
    # other proxy settings...
}

location /test/ {
    proxy_pass http://1.1.1.1:80/;
    # other proxy settings...
}

Nginx sees these as distinct endpoints and prefers the trailing-slash version for directory-like paths.

Here are three proven approaches to eliminate the unwanted redirect:

Option 1: Unified Location Block

Combine both variants into a single regex-based location:

location ~ ^/test(/|$) {
    proxy_pass http://1.1.1.1:80;
    proxy_redirect off;
    # preserve other proxy settings...
}

The regex matches both /test and /test/ without redirection.

Option 2: Absolute URI in proxy_pass

Modify the proxy_pass directive to include the full path:

location /test {
    proxy_pass http://1.1.1.1:80$request_uri;
    proxy_redirect off;
    # other settings...
}

This preserves the exact request URI including the slash presence.

Option 3: Disable Directory Processing

Add this directive to prevent Nginx from treating paths as directories:

location /test {
    proxy_pass http://1.1.1.1:80/;
    proxy_redirect off;
    absolute_redirect off;
    # other settings...
}

The absolute_redirect off prevents canonical URL formation.

After making changes, verify with:

nginx -t
curl -I http://foobar.com/test

You should see either a 200 response or a proxy to your backend without 301 redirects.

Each solution has different implications:

  • Regex locations have slightly higher CPU overhead
  • Absolute URIs in proxy_pass may reveal backend structure
  • Disabling directory processing might affect other legitimate redirects

For most proxy setups, Option 2 provides the best balance of simplicity and reliability.


When working with Nginx as a reverse proxy, you might encounter automatic 301 redirects that add trailing slashes to URIs. This happens because Nginx's default behavior treats /test and /test/ differently:

# Default behavior:
foobar.com/test → 301 → foobar.com/test/

Your observation is correct - Apache doesn't automatically redirect URIs without trailing slashes. This is because:

  • Apache's DirectorySlash directive defaults to On but handles things differently
  • Nginx's location matching rules are more strict about URI termination

The problem stems from how Nginx handles location blocks. In your current setup:

location /test {
    # This matches '/test' but not '/test/'
    proxy_pass http://1.1.1.1:80/;
}

location /test/ {
    # This matches '/test/' but not '/test'
    proxy_pass http://1.1.1.1:80/;
}

Nginx sees these as two distinct locations and applies its default normalization rules.

Here's the proper way to handle this proxy scenario:

server {
    listen 80;
    server_name fooBar.com;

    # Combined location that handles both cases
    location ~ ^/test(/|$) {
        proxy_redirect off;
        proxy_pass http://1.1.1.1:80/;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $http_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;
    }
}

If you're dealing with static files rather than proxying, you could use:

location /test {
    autoindex off;
    try_files $uri $uri/ =404;
}

After making changes, always:

  1. Test syntax: nginx -t
  2. Reload config: nginx -s reload
  3. Verify with curl: curl -I http://foobar.com/test

The response should show HTTP 200 instead of 301 when the trailing slash is omitted.