Table of Contents

Nginx installation and configuration on Debian

debian http nginx php cgi

This article describes the installation and configuration of Nginx on Debian. We will cover basic configuration and CGI management for PHP and C/Perl.

Nginx

The installation of nginx is pretty straighforward:

# aptitude install nginx

This creates the configuration directory into /etc/nginx. As usual with Debian, the main configuration file nginx.conf includes another repository for the virtual hosts: /etc/nginx/sites-enabled.

By default, this directory contains a virtual host that we're going to reuse for our configuration.

PHP 5 & Spawn-fcgi

Spawn-fcgi is a daemon that will call the php5-cgi program every time is receive a request on a socket.

It is initially part of the lighttpd project, but is also available alone in debian:

# apt-cache show spawn-fcgi
Package: spawn-fcgi             
Priority: extra            
Section: web             
Installed-Size: 96      
Maintainer: Jeremy Lal <kapouer@melix.org> 
Architecture: i386        
Version: 1.6.3-1             
Depends: libc6 (>= 2.1)   
Filename: pool/main/s/spawn-fcgi/spawn-fcgi_1.6.3-1_i386.deb
Size: 13210                
MD5sum: b2c2bc467e22d3ed01e9d134ba1b2c33              
SHA1: 312bd971908cddeb7f06d12520f105de86379f42                
SHA256: 50dc996d1c7219dc71ebb546bcdc47569b95e39793f7ad6ae35884d9151b42b9              
Description: A fastcgi process spawner
 spawn-fcgi allows fcgi processes to be separated from web server process :
   * Easy creation of chmoded socket.
   * Privilege separation without needing a suid-binary,
     or running a server as root.
   * You can restart your web server and the FastCGI applications
     without restarting the others.
   * You can run them in different chroot()s.
   * Running your FastCGI applications doesnâ??t depend on the web server
     you are running, which allows for easier testing of/migration
     to other web servers.    
Homepage: http://redmine.lighttpd.net/projects/spawn-fcgi
Tag: implemented-in::c, qa::low-popcon, role::program

So, let's install it, along with php5-cgi and some of its components:

# apt-get install spawn-fcgi php5-cgi php5-imap php5-gd php5-sqlite

<note tip>If your distribution doesn't ship spawn-fcgi, you can download the latest version of the source code from the official website and build it with a simple “./configure && make”. The binary is then located in the src/ subfolders, just copy spawn-fcgi in /usr/bin/ and you're done !</note>

Launch spawn-fcgi on port 9000

Spawn-fcgi will listen on tcp port 9000 and receive requests from nginx. To do so, we need to launch spawn-fcgi will the following command :

/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data /usr/bin/php5-cgi

It then creates a new process that listens on port 9000 and is attributed to user www-data (same as nginx).

minideb:/# netstat -tpan |grep 9000
tcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN      21621/php5-cgi
minideb:/# ps -edf|grep php
www-data 21621     1  0 17:24 ?        00:00:04 /usr/bin/php5-cgi
root     21869 21803  0 18:05 pts/1    00:00:00 grep php

You might want to set up an init script in /etc/init.d/php5cgi and link it for runtime in /etc/rc2.d. Below is the one I use:

#!/bin/bash
 
COMMAND=/usr/local/bin/spawn-fcgi
NAME=php5-cgi
ADDRESS=127.0.0.1
PORT=9000
USER=www-data
GROUP=www-data
PHPCGI=/usr/bin/php5-cgi
KILL=/bin/kill
 
case "$1" in
    start)
        start-stop-daemon --start \
                --exec $COMMAND -- -a $ADDRESS -p $PORT \
                -u $USER -g $GROUP -f $PHPCGI -P /var/run/$NAME.pid|| true
        echo -n "$NAME started with pid "
        cat /var/run/$NAME.pid
        echo
  ;;
    stop)
        if [ -e /var/run/$NAME.pid ]
        then
                $KILL `cat /var/run/$NAME.pid`
                rm /var/run/$NAME.pid
                echo "$NAME stopped"
        else
                echo "$NAME is not running, no pid file in /var/run/"
        fi
  ;;
    restart)
        $0 stop
        $0 start
  ;;
    *)
      echo "Usage: $0 {start|stop|restart}"
      exit 1
  ;;
esac
exit $RETVAL

PHP5-CGI configuration for Nginx

So, now that we have PHP in FastCGI mode ready, we need to tell nginx how to handle those php files.

Let's open this /etc/nginx/sites-enables/defaults virtual host and modify a few lines that are, for now, commented out.

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #              
        location ~ \.php$ {
                include fastcgi_params;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        }

Test the configuration before restarting the server :

# /etc/init.d/nginx configtest
Testing nginx configuration: the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful
nginx.

And then restart it with :

# /etc/init.d/nginx restart
Restarting nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful
nginx.

Great, now we create the file /var/www/nginx-default/index.php with the classic phpinfo() inside :

<?php
   phpinfo();
?>

Just open your favorite browser (links2, of course) and open the page :

$ links2 http://192.168.1.20:80/index.php

phpinfo() (p1 of 17)    PHP Logo 

PHP Version 5.2.11-1    

   System  Linux minideb 2.6.26-2-486 #1 Sun Jun 21 04:15:19 UTC 2009   
     i686   
   Build Date    Sep 20 2009 12:24:16     
   Server API    CGI/FastCGI  
   Virtual Directory Support disabled     
   Configuration File  /etc/php5/cgi
   (php.ini) Path 
   Loaded Configuration File /etc/php5/cgi/php.ini    
   Scan this dir for   /etc/php5/cgi/conf.d     
   additional .ini files
     /etc/php5/cgi/conf.d/gd.ini, /etc/php5/cgi/conf.d/imap.ini,  
   additional .ini files     /etc/php5/cgi/conf.d/pdo.ini,  
   parsed  /etc/php5/cgi/conf.d/pdo_sqlite.ini, 
     /etc/php5/cgi/conf.d/sqlite.ini,     
     /etc/php5/cgi/conf.d/suhosin.ini     
   PHP API 20041225     
   PHP Extension 20060613     
   Zend Extension220060519    
   Debug Build   no     
   Thread Safety disabled     
   Zend Memory Manager enabled
   IPv6 Support  enabled
   Registered PHP Streams    https, ftps, compress.zlib, compress.bzip2, php, file, data, 

It works ;)

FCGI Wrap

If the previous configuration is usefully for PHP websites, you might want to use scripts in differents langages. I personally use FCGI Wrap for C and Perl scripts. However, since it is not available in Debian, you need to build it yourself.

Get the source and build it

fcgiwrap is written by Grzegorz Nosek (gnosek). you can get the latest version from his github page as follow:

# wget http://github.com/gnosek/fcgiwrap/tarball/master
 
# tar -xzvf gnosek-fcgiwrap-ba8c8d9.tar.gz
gnosek-fcgiwrap-ba8c8d9/
gnosek-fcgiwrap-ba8c8d9/.gitignore
gnosek-fcgiwrap-ba8c8d9/Makefile.in
gnosek-fcgiwrap-ba8c8d9/README.rst
gnosek-fcgiwrap-ba8c8d9/configure.ac
gnosek-fcgiwrap-ba8c8d9/fcgiwrap.c

To build it, you will need the following packages:

# aptitude install autoconf make libfcgi-dev gcc

Now, simply launch the building commands:

# autoconf
 
# ./configure                                                                    
checking for gcc... gcc                                                                                                           
checking for C compiler default output file name... a.out                                                                         
checking whether the C compiler works... yes                                                                                      
checking whether we are cross compiling... no                                                                                     
checking for suffix of executables...                                                                                             
checking for suffix of object files... o
[...]
 
# make
gcc -std=gnu99 -Wall -Wextra -Werror -pedantic -O2 -g3 fcgiwrap.c -o fcgiwrap -lfcgi

And you will end up with a nice little binary called fcgiwrap. Just copy this binary into /usr/bin/.

Spawn-fcgi init script for fcgiwrap

The following script must be installed into /etc/init.d/ :

#!/bin/bash
 
COMMAND=/usr/bin/spawn-fcgi
NAME=fcgiwrap
ADDRESS=127.0.0.1
PORT=9001
USER=www-data
GROUP=www-data
CGI=/usr/bin/fcgiwrap
KILL=/bin/kill
 
case "$1" in
    start)
        start-stop-daemon --start \
                --exec $COMMAND -- -a $ADDRESS -p $PORT \
                -u $USER -g $GROUP -f $CGI -P /var/run/$NAME.pid|| true
        echo -n "$NAME started with pid "
        cat /var/run/$NAME.pid
        echo
  ;;
    stop)
        if [ -e /var/run/$NAME.pid ]
        then
                $KILL `cat /var/run/$NAME.pid`
                rm /var/run/$NAME.pid
                echo "$NAME stopped"
        else
                echo "$NAME is not running, no pid file in /var/run/"
        fi
  ;;
    restart)
        $0 stop
        $0 start
  ;;
    *)
      echo "Usage: $0 {start|stop|restart}"
      exit 1
  ;;
esac
exit $RETVAL

<note important>I assume that you have already installed spawn-fcgi. Otherwise, please go back there </note>

Now, launch the script and check that the socket is listening on TCP port 9001.

# /etc/init.d/spawnfcgiwrap start
spawn-fcgi: child spawned successfully: PID: 24818
fcgiwrap started with pid 24818
 
# netstat -taupen |grep 9001
tcp        0      0 127.0.0.1:9001          0.0.0.0:*               LISTEN      0          24109419    24818/fcgiwrap

Good to go, next we need to edit nginx server's configuration.

FCGI Wrap configuration for Nginx

This is very similar to what we did earlier with PHP. Nginx just needs for specific extension in the URI - in this case the files that end with .cgi - and send the requests for those files to the FCGI wrapper.

So, configuration of /etc/nginx/sites-availables/mysite looks like this :

server {
        listen   80;
        server_name  mysite;
 
        [....]
 
        location /tcgraph { 
                include /etc/nginx/fastcgi_params;
                if ($uri ~ "\.cgi$"){
                        fastcgi_pass   127.0.0.1:9001;
                }
        } 

Restart the nginx daemon and you're all set.

Test fcgiwrap

The basic perl script below can be used to test your configuration. Put it in your web directory and make sure it's executable.

#!/usr/bin/perl -w

use POSIX qw(uname);

my $host = (POSIX::uname())[1];

sub print_html()
{
        print "Content-Type: text/html\n\n";

        print <<HEADER;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Perl CGI test for $host</title>
<meta http-equiv="Pragma" content="no-cache" />
</head>
<body>
HEADER
                                                                                                                                  
        print "<h1>Perl CGI test for $host</h1>\n";  
        print "<p>script launched by user $ENV{USER}</p>";
        print <<FOOTER;
<hr/>
</body></html>
FOOTER
}

sub main()
{
	print_html;

}
 
main;

Once again, testing with links2 gives the following result:

 Perl CGI test for neuromatrice 

 Perl CGI test for neuromatrice
 
   script launched by user www-data
 

Configuration works ! :)