PHPFcgiExample
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: |
Contents |
[edit] 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.
[edit] 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.
[edit] 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.
- In the above script change "BIND=127.0.0.1:9000" to "BIND=/tmp/php.socket"
- In the Nginx config change "fastcgi_pass 127.0.0.1:9000;" to ""fastcgi_pass unix:/tmp/php.socket;"
[edit] 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.
[edit] 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.
[edit] 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.