Howto: nginx, php-fpm and serendipity weblog (s9y) with apache style rewriting

Nginx is (spoken: engine x) is a HTTP and reverse proxy server written by the russian developer Igor Sysoev. Nginx is also able to proxy mail server requests coming in via pop3 and imap.
Popular sites like FastMail.FM and WordPress.com are usually using nginx as reverse proxy or HTTP Server.
According to Netcrafts Web Server Survey from December 2009 nginx gained 5,2% market share since December 2008 and will exceed the 7% barrier in january 2010. In addition to that 12,9 million websites started using nginx as webserver or loadbalancer as first contact instance in the year 2010. Nginx now clearly exceeds the former lightwight webserver king lighttpd (market share round 0.36% in December 2009).

In a first example we use nginx is together with the FastCGI Process Manager php-fpm in order to run a serendipity weblog.
PHP-fpm will be included in future versions of PHP 5.3 but isn’t shipped with stable PHP 5.2 and therefore we have to built php and php-fpm the traditional way.

Installation description:

  • Install nginx from debian sources with the command
    apt-get install nginx

    (debian testing currently includes nginx in Version 0.7.63-1)

  • In addition to the depencies for building php we will need libevent installed in order to be able to compile php-fpm. Therefore we install it via
    apt-get install libevent-1.4-2 libevent-dev
  • In this example we are using the integrated compilation as described on the github page of php-fpm
  • Download php-fpm:
    wget "http://launchpad.net/php-fpm/master/0.6/+download/php-fpm-0.6~5.2.11.tar.gz"

  • Unpack it
    tar xzvf "php-fpm-0.6~5.2.11.tar.gz"

  • Generate fpm-patch
    php-fpm-0.6-5.2.11/generate-fpm-patch

  • Download php 5.2.12 stable:
    wget "http://us.php.net/get/php-5.2.12.tar.gz/from/us.php.net/mirror"

  • Unpack PHP:
    tar xvfz "php-5.2.12.tar.gz"

  • Apply the patch in php directory:
    cd php-5.2.12 && patch -p1 < ../fpm.patch

    patching file sapi/fpm/ac/fpm_build.m4
    patching file sapi/fpm/ac/fpm_checks.m4
    patching file sapi/fpm/ac/fpm_conf.m4
    patching file sapi/fpm/ac/fpm_libevent.m4
    patching file sapi/fpm/ac/Makefile.frag
    patching file sapi/fpm/cgi/cgi_main.c
    patching file sapi/fpm/cgi/CREDITS
    patching file sapi/fpm/cgi/fastcgi.c
    patching file sapi/fpm/cgi/fastcgi.h
    patching file sapi/fpm/cgi/getopt.c
    patching file sapi/fpm/cgi/php_getopt.h
    patching file sapi/fpm/conf/init.d.php-fpm.in
    patching file sapi/fpm/conf/nginx-site-conf.sample.in
    patching file sapi/fpm/conf/php-fpm.conf.in
    patching file sapi/fpm/config.m4
    patching file sapi/fpm/fpm/fpm_arrays.h
    patching file sapi/fpm/fpm/fpm_atomic.h
    patching file sapi/fpm/fpm/fpm.c
    patching file sapi/fpm/fpm/fpm_children.c
    patching file sapi/fpm/fpm/fpm_children.h
    patching file sapi/fpm/fpm/fpm_cleanup.c
    patching file sapi/fpm/fpm/fpm_cleanup.h
    patching file sapi/fpm/fpm/fpm_clock.c
    patching file sapi/fpm/fpm/fpm_clock.h
    patching file sapi/fpm/fpm/fpm_conf.c
    patching file sapi/fpm/fpm/fpm_conf.h
    patching file sapi/fpm/fpm/fpm_config.h
    patching file sapi/fpm/fpm/fpm_env.c
    patching file sapi/fpm/fpm/fpm_env.h
    patching file sapi/fpm/fpm/fpm_events.c
    patching file sapi/fpm/fpm/fpm_events.h
    patching file sapi/fpm/fpm/fpm.h
    patching file sapi/fpm/fpm/fpm_php.c
    patching file sapi/fpm/fpm/fpm_php.h
    patching file sapi/fpm/fpm/fpm_php_trace.c
    patching file sapi/fpm/fpm/fpm_php_trace.h
    patching file sapi/fpm/fpm/fpm_process_ctl.c
    patching file sapi/fpm/fpm/fpm_process_ctl.h
    patching file sapi/fpm/fpm/fpm_request.c
    patching file sapi/fpm/fpm/fpm_request.h
    patching file sapi/fpm/fpm/fpm_shm.c
    patching file sapi/fpm/fpm/fpm_shm.h
    patching file sapi/fpm/fpm/fpm_shm_slots.c
    patching file sapi/fpm/fpm/fpm_shm_slots.h
    patching file sapi/fpm/fpm/fpm_signals.c
    patching file sapi/fpm/fpm/fpm_signals.h
    patching file sapi/fpm/fpm/fpm_sockets.c
    patching file sapi/fpm/fpm/fpm_sockets.h
    patching file sapi/fpm/fpm/fpm_stdio.c
    patching file sapi/fpm/fpm/fpm_stdio.h
    patching file sapi/fpm/fpm/fpm_str.h
    patching file sapi/fpm/fpm/fpm_trace.c
    patching file sapi/fpm/fpm/fpm_trace.h
    patching file sapi/fpm/fpm/fpm_trace_mach.c
    patching file sapi/fpm/fpm/fpm_trace_pread.c
    patching file sapi/fpm/fpm/fpm_trace_ptrace.c
    patching file sapi/fpm/fpm/fpm_unix.c
    patching file sapi/fpm/fpm/fpm_unix.h
    patching file sapi/fpm/fpm/fpm_worker_pool.c
    patching file sapi/fpm/fpm/fpm_worker_pool.h
    patching file sapi/fpm/fpm/xml_config.c
    patching file sapi/fpm/fpm/xml_config.h
    patching file sapi/fpm/fpm/zlog.c
    patching file sapi/fpm/fpm/zlog.h
    patching file sapi/fpm/LICENSE
    patching file sapi/fpm/man/php-fpm.1.in
    patching file sapi/fpm/readme-ru.markdown

  • apply buildconf
    ./buildconf --force
    Forcing buildconf
    buildconf: checking installation...
    buildconf: autoconf version 2.13 (ok)
    rebuilding configure
    rebuilding main/php_config.h.in

  • Create new build directory and enter it
    mkdir fpm-build && cd fpm-build

  • Configure PHP with the options you need:
    ../configure --with-libevent=shared --enable-fastcgi --with-fpm --with-mcrypt --with-zlib --enable-mbstring --with-openssl --with-mysql --with-mysql-sock --with-gd --with-jpeg-dir=/usr/lib --enable-gd-native-ttf --without-sqlite --disable-pdo --enable-soap

  • Compliation and installation:
    make && make install

  • After installation, we have an init script in /etc/init.d/php-fpm and a default configuration in /etc/php-fpm.conf (symbolic link). We delete the symbolic link, copy the default config and edit it:
    rm /etc/php-fpm.conf
    cp /etc/php-fpm.conf.default /etc/php-fpm.conf
    vi /etc/php-fpm.conf

  • Please take a careful look at each configuration directive. We did the following changes to be compatible with our environment:
     <value name="listen_address">/dev/shm/php-fastcgi.socket</value>
    ...
    Set permissions for unix socket, if one used.
    In Linux read/write permissions must be set in order to allow connections from web server.
    Many BSD-derrived systems allow connections regardless of permissions.
    <value name="owner"></value>
    <value name="group"></value>
    <value name="mode">0666</value>
    Unix user of processes
     <value name="user">www-data</value>
    Unix group of processes
    <value name="group">www-data</value>
    ....
    <value name="max_children">100</value>

    Some of the values are taken from the PHP Highload Mailing List

  • update rc.d to start php-fpm upon server restart and start php-fpm initially
    update-rc.d php-fpm defaults; invoke-rc.d php-fpm start

  • That's it for the php part, we continue with nginx configuration, here an example as it is running on our site
    user www-data;
    worker_processes 2;

    error_log /var/log/nginx/error.log;
    pid /var/run/nginx.pid;

    events {
    worker_connections 1024;

    }

    http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request"' '$status $body_bytes_sent "$http_referer"' '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log;

    sendfile on;
    tcp_nodelay on;

    keepalive_timeout 10 10;
    gzip on;
    gzip_buffers 4 8k;
    gzip_proxied any;
    gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss;
    gzip_comp_level 1;
    gzip_http_version 1.0;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    }

    1024 worker connections and 2 worker processes should make a good start

  • sample vhost with serendipity rewrite (of course rewrite must be activated in s9y)
    server {
    listen yourip:80;
    server_name www.example.com example.com;
    root /var/www/virtual/example.com;
    index index.php;
    charset utf-8;

    error_log /var/log/nginx/example.com-error.log;
    access_log /var/log/nginx/example.com-access.log main;

    # Add expires header for static content
    if ($request_uri ~* "\.(ico|css|js|gif|jpe?g|png)\?[0-9]+$") {
    expires max;
    break;
    }

    location ~ \.php($|/) {
    set $script $uri;
    set $path_info "";

    if ($uri ~ "^(.+\.php)(/.+)") {
    set $script $1;
    set $path_info $2;
    }

    #fastcgi_pass 127.0.0.1:9000;
    fastcgi_pass unix:/dev/shm/php-fastcgi.socket;
    include /etc/nginx/fastcgi_params;
    fastcgi_param PATH_INFO $path_info;
    }

    location / {
    alias /var/www/virtual/example.com;
    rewrite ^/archives([/A-Za-z0-9]+)\.html /index.php?url=/archives/$1.html;
    rewrite ^/([0-9]+)[_\-][0-9a-z_\-]*\.html /index.php?url=$1-article.html;
    rewrite ^/feeds/(.*) /index.php?url=/feeds/$1;
    rewrite ^/unsubscribe/(.*)/([0-9]+) /index.php?url=/unsubscribe/$1/$2;
    rewrite ^/approve/(.*)/(.*)/([0-9]+) /index.php?url=approve/$1/$2/$3;
    rewrite ^/delete/(.*)/(.*)/([0-9]+) /index.php?url=delete/$1/$2/$3;
    rewrite ^/(admin|entries)(/.+)? /index.php?url=admin/;
    rewrite ^/archive$ /index.php?url=/archive;
    rewrite ^/categories/([0-9]+) /index.php?url=/categories/$1;
    rewrite ^/plugin/(.*) /index.php?url=plugin/$1;
    rewrite ^/search/(.*) /index.php?url=/search/$1;
    rewrite ^/authors/([0-9]+) /index.php?url=/authors/$1;
    rewrite ^/(.*\.html?)$ /index.php?url=/$1;
    rewrite ^/(serendipity\.css|serendipity_admin\.css)$ /index.php?url=/$1;
    rewrite ^/(index|atom|rss|b2rss|b2rdf).(rss|rdf|rss2|xml)$ /rss.php?file=$1&ext=$2;
    }
    }

    As you can see, we're using php-fastcgi.socket in shared memory, and not the tcp socket due to perfomance reasons.
    The rewrite rules are working if your s9y installation resides in your webserver root.
    If your're running it beneath, for example in /blog you need to change the lines accordingly:

    location /blog/ {
    alias /var/www/virtual/example.com/blog/;
    rewrite ^/blog/archives([/A-Za-z0-9]+)\.html /blog/index.php?url=/archives/$1.html;
    ....

  • Furthermore you need to add the following lines in your /etc/nginx/fastcgi_params file
    fastcgi_param SCRIPT_FILENAME $document_root$script;
    fastcgi_param SCRIPT_NAME $script;
    fastcgi_param PATH_INFO $path_info;

    This information is needed in order to find your php scripts beneath your document_root

  • That's it for nginx. Just restart the daemon via /etc/init.d/nginx restart and be happy.
    This constellation also works in conjunction with the available PHP accelerators like APC, XCache and eAccelerator.
  • If you´re getting errors/warnings during autoconf like this
    Forcing buildconf
    buildconf: checking installation...
    buildconf: autoconf version 2.64 (ok)
    buildconf: Your version of autoconf likely contains buggy cache code.
    Running vcsclean for you.
    To avoid this, install autoconf-2.13.
    Can't figure out your VCS, not cleaning.
    rebuilding configure

    try using autoconf2.13 (it should be available in apt-repository)

  • Related Posts: