Debugging Nginx Rewrite Loop: Fixing Internal Redirection Cycles for Missing Files


2 views

When dealing with static HTML sites on Nginx, a common pitfall is encountering infinite redirect cycles when trying to handle missing files. The configuration you've shared reveals several potential issues that could lead to this behavior.

server {
    listen       127.0.0.1:8080;
    server_name  .somedomain.com;
    root  /var/www/somedomain.com;

    location ~* \\.php.$ {
        include /etc/nginx/fastcgi.conf;
    }

    location / {
        root  /var/www/somedomain.com;
        try_files $uri $uri/ ;
    }

    error_page 404 /errors/404.html;
    location /errors/ {
        alias /var/www/errors/;
    }
}

The infinite redirect cycle occurs because of how Nginx processes the try_files directive combined with the error page handling. Here's what's happening:

  1. When a non-existent file is requested, Nginx checks $uri then $uri/
  2. When both fail, it triggers the 404 error page
  3. The 404 error page path gets processed through the same try_files logic
  4. This creates an endless loop

Here's the fixed configuration:

server {
    listen       127.0.0.1:8080;
    server_name  .somedomain.com;
    root  /var/www/somedomain.com;

    location ~* \\.php$ {
        include /etc/nginx/fastcgi.conf;
        fastcgi_intercept_errors on;
    }

    location / {
        try_files $uri $uri/ =404;
    }

    location = /errors/404.html {
        internal;
        alias /var/www/errors/404.html;
    }

    error_page 404 /errors/404.html;
}
  • Added =404 to try_files to properly terminate the search
  • Made the 404.html location block internal-only
  • Fixed the PHP regex pattern (removed trailing dot)
  • Added fastcgi_intercept_errors on for PHP files

To verify the fix works:

curl -I http://somedomain.com/nonexistent-page.html

Should return HTTP 404 with your custom error page, without any redirect loops.

Consider these additional improvements:

# Cache error pages
location /errors/ {
    alias /var/www/errors/;
    expires 30d;
    access_log off;
}

# Handle common static files more efficiently
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 365d;
    add_header Cache-Control "public";
}

When testing a static HTML site with Nginx, you might encounter an infinite redirect loop when accessing non-existent PHP files, despite having proper error_page directives configured. The error log typically shows:

2023/03/15 10:00:00 [error] 1234#0: *1 rewrite or internal redirection cycle while internally redirecting to "/errors/404.html"

The core issue stems from how Nginx processes try_files and error_page directives. Let's examine the problematic config:

server {
    listen       127.0.0.1:8080;
    server_name  .somedomain.com;
    root  /var/www/somedomain.com;

    location ~* \.php.$ {
        include /etc/nginx/fastcgi.conf;
    }

    location / {
        root  /var/www/somedomain.com;
        try_files $uri $uri/ ;
    }

    error_page 404 /errors/404.html;
    location /errors/ {
        alias /var/www/errors/;
    }
}

The redirect cycle occurs because:

  1. Nginx first tries to serve the PHP file via the location block
  2. When not found, it falls back to the / location with try_files
  3. The 404 error then redirects to /errors/404.html
  4. This new request gets caught in the same cycle

Here's the corrected configuration that prevents the loop:

server {
    listen       127.0.0.1:8080;
    server_name  .somedomain.com;
    root  /var/www/somedomain.com;

    # Handle PHP files explicitly
    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/var/run/php-fpm.sock;
    }

    # Static files
    location / {
        try_files $uri $uri/ =404;
    }

    # Custom error page outside root
    location = /404.html {
        root /var/www/errors;
        internal;
    }

    error_page 404 /404.html;
}
  • Added explicit =404 fallback in try_files
  • Separated error document handling with internal directive
  • More specific PHP file matching with \.php$
  • Explicit fastcgi_pass directive for PHP processing

Verify the fix by:

curl -I http://somedomain.com/nonexistent.php
# Should return 404 with your custom page

Check your error logs to confirm the redirect loop is resolved:

tail -f /var/log/nginx/somedomain.com-error.nginx.log