Creating a (relatively) secure dynamic PHP test environment with nginx

I’ve been working on a deployment tool for WordPress development environments; one that can quickly provision and maintain the dizzying array of sites needed to test plugins against different versions of WordPress (with and w/o Multisite), BuddyPress, bbPress, etc.

Some cocktail napkin math suggested at least 20 combinations were needed to cover all the currently-supported configurations. Besides the chore of setting up environments for each, the need to add or remove environments as components are updated makes a manual deployment effort seem ridiculous.

While I’m finishing up work on that tool, I thought I’d start by posting the web server and PHP configs I’m using to make this happen.

nginx Configuration

I went with nginx because of its low memory usage and its handy regular-expression hostname matching, which I find more flexible and less kludgey for this application than mod_vhost_alias or mod_rewrite. This nginx config will provide the flexibility of dynamically-creating sites while still providing some isolation between them.

server {
        server_name ~^(.*)\.dev.your.domain$ ;
        set $instance $1;
        set $session_root /var/www/dev/$instance/sessions;
        root /var/www/dev/$instance/html;
        index index.php;
        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/fpm-devsites.sock;
#               fastcgi_pass
                fastcgi_param PATH_INFO $fastcgi_path_info;
                fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                fastcgi_param PHP_ADMIN_VALUE  "open_basedir=$document_root\nsession.save_path=$session_root";
                include /etc/nginx/fastcgi_params;
        location / {
                try_files $uri $uri/ /index.php;

Thanks to mike at for the great guide to the PHP_ADMIN_VALUE param.

PHP-FPM Configuration

Ideally each site would have its own FPM pool, but that would mean root privileges were needed to provision new sites. So a single but separate pool for the dev environments was a necessary compromise. The goal of dynamically creating sites through an unprivileged web interface also meant that the devsites pool needed to run as the web user.

; Start a new pool named 'www'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = www-data
group = www-data
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   ''    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /var/run/fpm-$pool.sock
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
;                 mode is set to 0666
listen.owner = www-data = www-data
listen.mode = 0666

If you’ve found this helpful (or troublesome), feel free to drop me a line in the comments below!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>