FAQ

Page Discussion History

PHPFcgiExampleOld

Revision as of 07:04, 6 May 2013 by Zakaria (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
This page needs some love! Are you qualified to bring it up to the standards of our documentation?

This page has been recognized as a useful topic for the wiki, but is unsuitable in its current state for the following reason:

This page is severely outdated and suggest bad practice. PHP now ships with php-fpm that is much preferred.


Contents

FastCGI Example

First thing, I recommend keeping all your typical FCGI settings in a single file and importing them.

For example you might have an /etc/nginx/fastcgi.conf (or /etc/nginx/fastcgi_params: installed by default on debian) file that looks like this:

#fastcgi.conf
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

This allows you to keep your individual FastCGI location blocks as simple as possible.

Spawning a FastCGI Process

Unlike Apache or Lighttpd, Nginx does not automatically spawn FCGI processes. You must start them separately. In fact, FCGI is a lot like proxying. There's a few ways to start FCGI programs, but luckily PHP5 will auto-spawn as many as you set in the PHP_FCGI_CHILDREN environment variable. First, install the php5-cgi library. Then, we can simply run php-cgi -b 127.0.0.1:9000 manually, or create an init script like this (for Debian 6.0):

#!/bin/bash
### BEGIN INIT INFO
# Provides:          php-fcgi
# Required-Start:    $nginx
# Required-Stop:     $nginx
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts php over fcgi
# Description:       starts php over fcgi
### END INIT INFO
 
(( EUID )) && echo ‘You need to have root priviliges.’ && exit 1
BIND=127.0.0.1:9000
USER=www-data
PHP_FCGI_CHILDREN=15
PHP_FCGI_MAX_REQUESTS=1000
 
PHP_CGI=/usr/bin/php-cgi
PHP_CGI_NAME=`basename $PHP_CGI`
PHP_CGI_ARGS="- USER=$USER PATH=/usr/bin PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND"
RETVAL=0
 
start() {
      echo -n "Starting PHP FastCGI: "
      start-stop-daemon --quiet --start --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS
      RETVAL=$?
      echo "$PHP_CGI_NAME."
}
stop() {
      echo -n "Stopping PHP FastCGI: "
      killall -q -w -u $USER $PHP_CGI
      RETVAL=$?
      echo "$PHP_CGI_NAME."
}
 
case "$1" in
    start)
      start
  ;;
    stop)
      stop
  ;;
    restart)
      stop
      start
  ;;
    *)
      echo "Usage: php-fastcgi {start|stop|restart}"
      exit 1
  ;;
esac
exit $RETVAL

Save this to /etc/init.d/ (or wherever your init scripts are) as php-fcgi. Install the usual way (e.g. for Debian/Ubuntu: insserv php-fcgi or update-rc.d php-fcgi defaults) and start it.

Using a Unix Socket

The above example binds PHP to run on port 9000 on localhost (127.0.0.1:9000). This is the most common and easiest to understand option. Unix sockets are however much better. To use them you need to do two things.

  1. In the above script change "BIND=127.0.0.1:9000" to "BIND=/tmp/php.socket"
  2. In the Nginx config change "fastcgi_pass 127.0.0.1:9000;" to ""fastcgi_pass unix:/tmp/php.socket;"

Connecting Nginx to the running FastCGI Process

Now that the FCGI process is running, we must tell Nginx to proxy requests to it via the FCGI protocol:

location ~ \.php$ {
  # Filter out arbitrary code execution
  location ~ \..*/.*\.php$ {return 404;}
 
  include fastcgi_params;
  fastcgi_pass  127.0.0.1:9000;
}

Restart Nginx.

Secure your upload directory!!

Too many example configs fail to secure the "uploads" directory of the application. Remember that if someone can upload a file named xyz.php and the uploads dir is publically accessible then you have given the attacker an easy way to insert PHP onto your site...

So if your app has an upload dir "/images/" then insert if ($uri !~ "^/images/") before fastcgi_pass, as so:

location ~ \.php$ {
  include /etc/nginx/fastcgi_params;
  if ($uri !~ "^/images/") {
    fastcgi_pass 127.0.0.1:9000;
  }
}

As "if" is considered evil now, there's an updated alternative available to those looking to secure their upload directory:

location ~* (^(?!(?:(?!(php|inc)).)*/blogs\.dir/).*?(php|inc)) {
  try_files $uri = 404;
  fastcgi_split_path_info ^(.+.php)(.*)$;
  fastcgi_pass unix:/var/run/php-fpm.socket;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  include fastcgi_params;
}

Be aware that this code example also makes use of the Zero-day exploit defense as well as an Unix socket for PHP. The most relevant bit for this issue is the location-line. Remember: you need to set /blogs\.dir/ to the directory-name you actually want to exclude.

Common Errors

If the following error is returned to you when you try to visit a PHP file, it is because FastCGI does not know where your document_root is located.

No input file specified.

First, check to make sure your FCGI parameter for SCRIPT_FILENAME is set to include the $document_root variable. The current fastcgi_params file provided by the debian package does not include the document root in the script filename parameter. It should look like the example below.

# Located with your other FastCGI parameters, possibly in:
# /etc/nginx/fastcgi.conf or /etc/nginx/fastcgi_params
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

If this is not the problem, then your nginx server configuration is probably missing the document root setting (or it is misplaced). This setting is configured through the "root" variable. Below is an example of a properly configured server block. Notice that the root is outside of the location block.

server {
        listen   80 default;
        server_name  localhost;
 
        access_log  /var/log/nginx/localhost.access.log;
        root /var/www/nginx-default;
 
        location / {
                index  index.html index.htm;
        }
 
        location ~ \.php$ {
            include /etc/nginx/fastcgi.conf;
            fastcgi_pass unix:/tmp/php.socket;
        }
}

Once you have updated your nginx configuration, restart nginx and try reloading the PHP page -- FastCGI should now be able to locate the script and interpret it.

If this does not solve your problem, see pitfalls for other common errors.