Fix Nginx ERR_TOO_MANY_REDIRECTS: Solving the 'Internal Redirection Cycle'

intermediate Nginx2026-03-30| Ubuntu 20.04/22.04, Debian 11/12, CentOS 7/8, Nginx 1.18.x or higher, PHP-FPM 7.4/8.x

Error Message

rewrite or internal redirection cycle while internally redirecting to "/index.php"
#nginx#devops#php-fpm#server-config#troubleshooting

The ProblemYou go to check your site, but instead of the homepage, your browser hits you with a frustrating ERR_TOO_MANY_REDIRECTS message. When you dig into your Nginx error logs (usually at /var/log/nginx/error.log), you will likely see a specific entry like this:

2023/10/24 10:15:30 [error] 1234#0: *1 rewrite or internal redirection cycle while internally redirecting to "/index.php", client: 192.168.1.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"

Nginx has a safety limit of 10 internal redirects. If a request gets stuck in a loop and never finds a final file to serve, Nginx kills the process to prevent your server's CPU from Maxing out.

Step 1: Pinpoint the Loop with CurlA quick curl test reveals whether the loop lives in the browser's logic or deep inside Nginx. Run this command from your terminal:

curl -I http://example.com

Look closely at the results. If you see dozens of HTTP/1.1 301 Moved Permanently headers jumping between HTTP and HTTPS, you have a logical redirect loop. However, if you see a 500 Internal Server Error, you are dealing with the internal rewrite cycle mentioned in your logs.

Step 2: Audit the try_files DirectiveMost PHP applications—like Laravel, WordPress, or Symfony—rely on the try_files directive to handle URLs. A typical, healthy configuration looks like this:

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

The cycle starts when Nginx cannot find index.php in your root folder. Because the file is missing, Nginx falls back to the last parameter, which triggers the location ~ \.php$ block, fails again, and restarts the search. Double-check your root path. If your app lives in /var/www/my-app/public but Nginx is looking in /var/www/my-app, it will never find the entry file.

server {
    listen 80;
    server_name example.com;
    root /var/www/html/public; # Ensure this path contains index.php
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
    }
}

Step 3: Resolve SSL Termination LoopsAre you using Cloudflare or an AWS Load Balancer? You might be caught in an SSL "ping-pong" match. This happens when your proxy talks to Nginx via port 80 (HTTP), but Nginx insists on redirecting everything to HTTPS. The proxy sends a request, Nginx says "Go to HTTPS," and the proxy sends it back as HTTP again.

To stop this, use the X-Forwarded-Proto header to detect the original protocol. Update your config to only redirect if the request wasn't already secure:

# Add this inside your server block
if ($http_x_forwarded_proto != "https") {
    return 301 https://$host$request_uri;
}

Step 4: Use a 'Circuit Breaker' for PHPSometimes Nginx gets confused about which script to execute if the SCRIPT_FILENAME is missing. Adding a circuit breaker is the best way to stop a loop cold. Add try_files $fastcgi_script_name =404; inside your PHP block. This tells Nginx to stop and throw a 404 error if the file isn't found, rather than attempting another internal redirect.

location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    
    # This line breaks the loop if the file is missing
    try_files $fastcgi_script_name =404;
    
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/var/run/php/php-fpm.sock;
}

Step 5: Verify and ReloadTest your changes before you apply them to avoid taking your site offline. Run the Nginx syntax check:

sudo nginx -t

If the test passes, reload the service to put the changes into effect:

sudo systemctl reload nginx

Finally, run curl -I again. You should now see a clean HTTP/1.1 200 OK or a single 301 redirect followed by a 200 status code.

Summary Checklist- Check the Root: Ensure the root directive points exactly to the folder containing your index.php.- Stop the Search: Use try_files ... =404; in PHP blocks to prevent Nginx from searching indefinitely for missing files.- Proxy Awareness: If you use Cloudflare, rely on X-Forwarded-Proto headers instead of the $scheme variable.- Enable Debugging: If you are still stuck, add rewrite_log on; to your config and set the error log level to notice to see the step-by-step URL transformations.

Related Error Notes