In a previous post, we had written about setting up Perl-FastCGI on Nginx.
This article is about setting up PHP-FastCGI. As always, this article assumes
that you are running on Debian Squeeze (or above) and uses aptitude/apt-get
for fetching and installing packages.
Install these packages :
apt-get install nginx php5-cgi
We’ll now replace Nginx’s package maintainer’s vhosts file with our own.
mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.old
vi /etc/nginx/sites-available/default
Put this in the file :
server {
listen [::]:80;
server_name example.com; # Replace this with your own
root /var/www/example.com;
index index.html index.htm index.php;
access_log /var/www/logs/example.com.access.log;
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/tmp/php.socket;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Now, there are some tutorials on the internet which wrongly configure Nginx and introduce an arbitrary code execution bug. Note that Nginx passes a request to the php-cgi daemon if the REQUEST_URI
has a trailing .php
. It does not check whether the file exists or not. Suppose a request is made for /images/dog.jpg/test.php
. Nginx will match the trailing .php
and pass the request to php-cgi. Now, if test.php does not exist and cgi.fix_pathinfo
is set to 1, php-cgi will try to execute /images/dog.jpg
with /test.php
as pathinfo. This is especially dangerous if you have a public upload directory. In our configuration, this will not happen even if cgi.fix_pathinfo
is set to 1. The try_files $uri =404;
line checks for the existence of the .php file and returns a 404 response header if it does not exist. Alternatively, cgi.fix_pathinfo
can be set to 0, but is is not required as our configuration takes care of it.
Now we’ll use a Debian init script to control the php-cgi daemon which will be
running on port 9000.
vi /etc/init.d/php-fcgi
Add this in the file :
#!/bin/bash
### BEGIN INIT INFO
# Provides: php-cgi
# Required-Start: networking
# Required-Stop: networking
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start the PHP FastCGI daemon.
### END INIT INFO
BIND=/tmp/php.socket
USER=www-data
PHP_FCGI_CHILDREN=2
PHP_FCGI_MAX_REQUESTS=5000
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-fcgi {start|stop|restart}"
exit 1
;;
esac
exit $RETVAL
This can also be done in one line if you have shell access on your server :
wget http://nginxlibrary.com/downloads/php-fcgi/php-fcgi -O /etc/init.d/php-fcgi
Give the file executable permissions and make it start during booting :
chmod +x /etc/init.d/php-fcgi && insserv php-fcgi
Finally, start the fastcgi daemon :
invoke-rc.d php-fcgi start
To check that it is working properly, create this file :
vi /var/www/example.com/info.php
Put this in it :
<?php phpinfo(); ?>
Now go to http://example.com/info.php
and you’ll get the PHP information
page showing information about the loaded modules.
Now, there are a couple of options you can change in the PHP-FastCGI init
script, namely, PHP_FCGI_CHILDREN
and PHP_FCGI_MAX_REQUESTS
.
PHP_FCGI_CHILDREN
specifies the number of child processes the php-cgi master
process will spawn. Usually, two, or, even one is sufficient for a medium-
traffic site. PHP_FCGI_MAX_REQUESTS
specifies the number of requests each
child process will serve before being terminated and respawned.