rewrite or internal redirection cycle while internally redirecting to, in nginx

5,736

There are two common ways of using Nginx with PHP projects:

Nginx serves static files, PHP processing is done by PHP-FPM daemon and proxied by Nginx using FastCGI

This is a common scenario to setup a dedicated server to get best performance and you should use it unless your application heavily relies on some Apache's features like .htaccess and you cannot write this once and for all inside Nginx config.

server {
    ...
    root /home/test/web/foo.co.uk/public_html;
    fastcgi_index index.php;

    location / { try_files $uri $uri/ /index.php?$args; }
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9000;
    }

    location ~* ^.+\.(jpg|jpeg|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|exe|pdf|doc|xls|ppt|txt|odt|ods|odp|odf|tar|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm)$ {
        access_log     /var/log/apache2/domains/foo.co.uk.log combined;
        access_log     /var/log/apache2/domains/foo.co.uk.bytes bytes;
        expires        max;
    }
}

Nginx serves static files, everything else is proxied to an internal Apache daemon

In this case PHP processing is done by Apaches mod_php module. The setup is something like this:

server {
    ...
    root /home/test/web/foo.co.uk/public_html;
    location / { proxy_pass https://178.79.134.35:8443; }
    location ~* ^.+\.(jpg|jpeg|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|exe|pdf|doc|xls|ppt|txt|odt|ods|odp|odf|tar|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm)$ {
        access_log     /var/log/apache2/domains/foo.co.uk.log combined;
        access_log     /var/log/apache2/domains/foo.co.uk.bytes bytes;
        expires        max;
    }
}

There is no need in fallback code for static files unless you want your apache to generate a heavy 404 page. This also can be set up in nginx with error_page directive.

Also, in this case please consider connecting to Apache on a loopback interface on a plain HTTP port fo better performance. HTTPS should be set up on the front-end web server only. Proxying to a HTTPS-enabled port on the same host does not really give you any benefit.

UPDATE: I used TCP socket connection to PHP-FPM in my examples just for the sake of example. However there are many benchmarks on the Internet that showed better performance for local UNIX sockets rather than TCP sockets. For Nginx it's fairly easy to use UNIX socket as well:

fastcgi_pass unix:/run/php/php7.0-fpm.sock;

Make sure to check access permissions to that file and corresponding path.

Share:
5,736

Related videos on Youtube

Andrew Newby
Author by

Andrew Newby

Updated on September 18, 2022

Comments

  • Andrew Newby
    Andrew Newby almost 2 years

    I'm trying to get a rewrite rule to work for nginx and Wordpress. After a reboot of nginx, I try the site and get an IS 500 error page. The log file says:

    *1 rewrite or internal redirection cycle while internally redirecting to "/index.php"
    

    I'm confused, as this is how they tell you to do it in all the tutorials (and even the Wordpress documentation for setting up rewrite rules with nginx):

        try_files $uri $uri/ /index.php?$args ;
    

    Here is the whole server block:

    server {
        listen      178.79.134.35:443 http2;
        listen      [::]:443 http2;
        server_name foo.co.uk www.foo.co.uk cdn.foo.co.uk;
        ssl         on;
        ssl_certificate      /home/test/conf/web/ssl.foo.co.uk.pem;
        ssl_certificate_key  /home/test/conf/web/ssl.foo.co.uk.key;
        error_log  /var/log/apache2/domains/foo.co.uk.error.log error;
    
        location / {
    
            # This is cool because no php is touched for static content.
            # include the "?$args" part so non-default permalinks doesn't break when using query string
            try_files $uri $uri/ /index.php?$args ;
    
            proxy_pass      https://178.79.134.35:8443;
            location ~* ^.+\.(jpg|jpeg|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|exe|pdf|doc|xls|ppt|txt|odt|ods|odp|odf|tar|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm)$ {
                root           /home/test/web/foo.co.uk/public_html;
                access_log     /var/log/apache2/domains/foo.co.uk.log combined;
                access_log     /var/log/apache2/domains/foo.co.uk.bytes bytes;
                expires        max;
                try_files      $uri @fallback;
            }
        }
    
        location /error/ {
            alias   /home/test/web/foo.co.uk/document_errors/;
        }
    
        location @fallback {
            proxy_pass      https://178.79.134.35:8443;
        }
    
        location ~ /\.ht    {return 404;}
        location ~ /\.svn/  {return 404;}
        location ~ /\.git/  {return 404;}
        location ~ /\.hg/   {return 404;}
        location ~ /\.bzr/  {return 404;}
    
        include /home/test/conf/web/snginx.foo.co.uk.conf*;
    }
    

    Anyone got any suggestions? I even tried something a bit more messy, but get the same error:

        if (!-e $request_filename){
            rewrite ^(.*)$ /index.php?q=$1 last;
            break;
        }
    

    I've tweaked it a bit, so it does a pass to

    location / {
    
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /home/test/web/foo.co.uk/public_shtml$fastcgi_script_name;
        include fastcgi_params;
    
        try_files $uri $uri/ /index.php?$args ;
    
        proxy_pass      https://178.79.134.35:8443;
    
        location ~* ^.+\.(jpg|jpeg|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|exe|pdf|doc|xls|ppt|txt|odt|ods|odp|odf|tar|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm)$ {
            root           /home/test/web/foo.co.uk/public_shtml;
            access_log     /var/log/apache2/domains/foo.co.uk.log combined;
            access_log     /var/log/apache2/domains/foo.co.uk.bytes bytes;
            expires        max;
            try_files      $uri @fallback;
        }
    }
    

    UPDATE 2: As suggested, I know how a location ~ \.php$ { } block wrapped around it. So it looks like:

    location / {
    
        proxy_pass      https://178.79.134.35:8443;
    
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME home/test/web/foo.co.uk/public_shtml$fastcgi_script_name;
            include fastcgi_params;
        }
    
        location ~* ^.+\.(jpg|jpeg|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|exe|pdf|doc|xls|ppt|txt|odt|ods|odp|odf|tar|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm)$ {
            root           /home/test/web/foo.co.uk/public_shtml;
            access_log     /var/log/apache2/domains/foo.co.uk.log combined;
            access_log     /var/log/apache2/domains/foo.co.uk.bytes bytes;
            expires        max;
            try_files      $uri @fallback;
        }
    
        try_files $uri $uri/ /index.php?$args ;
    }
    

    but I now get an error:

    111: Connection refused) while connecting to upstream, client: 81.174.134.133, server: foo.co.uk, request: "GET /wp-admin/ HTTP/2.0", upstream: "fastcgi://127.0.0.1:9000"

    Looking up on Google, and people suggest testing port 9000:

    root@com:/home/# telnet localhost 9000
    Trying ::1...
    Trying 127.0.0.1...
    telnet: Unable to connect to remote host: Connection refused
    

    So it doesn't seem to be able to access it. I'm a bit confused though, as the firewall is setup to allow port 9000:

    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:9000
    

    UPDATE 2: As suggested, I tried:

    netstat -nltp|grep 9000

    ...but it gives no result. If I look though, I can see php-7.0-fpm installed and running (as far as I can tell);

    root@com:~# service php7.0-fpm status
    â php7.0-fpm.service - The PHP 7.0 FastCGI Process Manager
       Loaded: loaded (/lib/systemd/system/php7.0-fpm.service; enabled; vendor preset: enabled)
       Active: active (running) since Wed 2017-04-12 12:06:48 UTC; 18h ago
      Process: 3560 ExecStartPre=/usr/lib/php/php7.0-fpm-checkconf (code=exited, status=0/SUCCESS)
     Main PID: 3800 (php-fpm7.0)
       Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
       CGroup: /system.slice/php7.0-fpm.service
               ââ3800 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf)
               ââ3872 php-fpm: pool www
               ââ3882 php-fpm: pool www
    
    Apr 12 12:06:43 com.x.com systemd[1]: Starting The PHP 7.0 FastCGI Process Manager...
    Apr 12 12:06:48 com.x.com systemd[1]: Started The PHP 7.0 FastCGI Process Manager.
    

    I looked in /etc/php/7.0/fpm/php-fpm.conf, but don't see any reference to a "listen" port

    UPDATE 3: I found a post where they said about editing the www.conf file: https://github.com/serghey-rodin/vesta/issues/1025

    So looking for that on my server, I found:

    /etc/php/7.0/fpm/pool.d/www.conf

    I then commented out:

    listen = /run/php/php7.0-fpm.sock
    

    ...and added:

    listen = 9000
    

    ...and then rebooted with:

    service php7.0-fpm restart

    And now I can see it on the netstat

    I then made sure I had this in my config:

        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /home/test/web/foo.co.uk/public_shtml$fastcgi_script_name;
            include fastcgi_params;
        }
    

    rebooted nginx, and voila :) It lives!

    • Admin
      Admin about 7 years
      You are missing any support for PHP. See this page for guidance.
    • Admin
      Admin about 7 years
      @RichardSmith - I've added that in, but it doesn't seem to have helped (same error message). I've updated my opening post with the new location { }
    • Admin
      Admin about 7 years
      You still lack the location that would process PHP. Should be something like location ~ \.php$ { ... fastcgi_pass unix;/var/run/php5-fpm.sock; ... }
    • Admin
      Admin about 7 years
      @jollyroger - thanks. Can't believe I missed that! I'm still getting a 111 error (see my updated post above). I thought maybe it was the firewall not letting it through, but it seems to be an open port :/
    • Admin
      Admin about 7 years
      @jollyroger - is there any way I can just forward the scripts to my Apache install on 178.79.134.35:8443 ? PHP works fine in Apache, but I can't seem to get this bloomin port opened up and working with nginx :/
    • Admin
      Admin about 7 years
      Nginx does not have mod_php as apache does. In order to process PHP scripts it needs a separate process called PHP-FPM. I'd still recommend using php-fpm directly over apache setup. Please check if php-fpm(maybe called php5-fpm or similar, look for FPM) is installed/configured/running on your host or at least check if anyone listens on port 9000 (netstat -nltp|grep 9000).
    • Admin
      Admin about 7 years
      @jollyroger - thanks. That actually didn't give a result, so looks like its not running for some reason. The weird thing, is that I do see it: root 3671 0.0 0.2 83660 5404 ? Ss Apr12 0:02 php-fpm: master process (/usr/local/vesta/php/etc/php-fpm.conf), but its just not listening for some reason
    • Admin
      Admin about 7 years
      @jollyroger - I just checked, and it looks like php7.0-fpm is running, but just not listening. I've updated my post above
  • Andrew Newby
    Andrew Newby about 7 years
    Thanks for your detailed response :) The issue was actually due to the fact php-fpm was running on a socket, instead of listening on the port. Fixed that up, and it works like a charm now. Maybe add that into your answer so I can accept it?
  • Miguel Diaz Ballesteros
    Miguel Diaz Ballesteros about 7 years
    I added info on using unix sockets for nginx/php-fpm. This should encourage readers to use correct setup from the start. Hope this suits you as well.
  • Andrew Newby
    Andrew Newby about 7 years
    thanks. I ended up doing it a different way in the end as well :) Instead of an nginx + apache2 setup, I went with nginx + php-fpm, and then have Apache as a backend (purely for my Perl stuff, so I can run it under mod_perl). Seems to be working perfectly now, and even the Wordpress updates stuff works well now <G>