Setting Up PHP behind Nginx with FastCGI

The traditional way of running PHP is with Apache HTTP Server using mod_php. In fact, mod_php was the most popular Apache module up until 2009 when that claim went to mod_ssl. But as the Internet grew and the technologies that power it evolved, other ways of serving PHP sites and applications became available. nginx, a server written to solve the C10k problem, is eating into Apache’s marketshare, and running PHP behind nginx with FastCGI is becoming an increasingly commonplace alternative.

Configuring a PHP and nginx setup is a bit more involved than the traditional Apache one, although it has become easier in the recent past with better packaging and default configurations. This article will guide you through the process of configuring PHP and nginx for serving up your own high performance website. I assume you’re working on Ubuntu Server (I’m using Ubuntu 13.04, 64-bit), and we’ll install the core applications using Apt.

FastCGI in a Nutshell

In the early days of the web, developers needed a way to run scripts on the server to generate page content and spawn other processes, and so the CGI (Common Gateway Interface) protocol was devised as a means to facilitate this.

CGI defines the environment (environment variables, request-specific variables, etc.) a script will execute in, and how data is passed between the script and the web server. For each request, CGI sets up the environment, spawns a running instance of the script and passes any incoming data to it, and captures and sends the script’s output to the server.

nginx-fcgi-1a

Of course, CGI has its pros and cons. On the pro side, CGI is language-independent, meaning scripts can be written in any programming language the developer is comfortable with (for example: Perl, C, bash, etc.), and each execution runs in isolation from the web server which prevents bugs in a script from potentially crashing the entire web stack. On the con side, a new process is created for each request which can be CPU-intensive and time-consuming.

FastCGI is essentially CGI with some enhancements that addresses CGI’s shortcomings. It reduces time/CPU-overhead by using a persistent process to execute scripts, and data between the web server and FastCGI is passed using sockets which encourages more-scalable service architectures (server farms and load balancing, asynchronous communication between web server and FastCGI, etc.).

For more information on FastCGI, check out the white paper on the FastCGI website, and for PHP’s implementation specifically check out the PHP manual.

Basic Installation and Configuration

To install PHP via Apt, execute the following at the command-line:

sudo apt-get install php5-cli php5-fpm

Then, to install nginx, execute:

sudo apt-get install nginx

Apt will identify the required dependencies in each case and prompt for confirmation to continue. Once granted, Apt will download and install the requested packages and their dependencies.

Ubuntu places nginx’s configuration files in /etc/nginx and its sub-directories. Shared configuration fragments are kept in that root, and specific server setups reside in sites-available with symlinks in sites-enabled to make them active.

It’s a good idea to avoid editing the original configuration files, so I suggest using copies and keeping the originals pristine. This way, you can configure nginx to your liking and not worry about your efforts being over-written by Apt during future upgrades. You also have the original default configuration to refer back to if you never need to.

Delete the symlink in sites-enabled, duplicate the default file in sites-available, and create a new symlink.

cd /etc/nginx
sudo rm sites-enabled/default
sudo cp sites-available/default sites-available/my-default
sudo ln -s /etc/nginx/sites-available/my-default sites-enabled/default

To have nginx proxy PHP requests to the FastCGI service, open the freshly duplicated configuration file and locate the section that starts with:

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {

Uncomment the location line and it’s matching close brace, the fastcgi_split_path_info line, and the lines that pertain to running with php5-fpm. When you’re done, the section should look like this:

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
#   # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
#
#   # With php5-cgi alone:
#   fastcgi_pass 127.0.0.1:9000;
#   # With php5-fpm:
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
}

Save your changes, and then start nginx.

sudo service nginx start

Connect to the server with your browser and you should see nginx’s welcome screen.

nginx-fcgi-2

The default configuration specifies /usr/share/nginx/html as the web root. In that directory, create a file named info.php with a call to phpinfo() and then load it in your web browser to verify nginx can communicate with PHP. In the output, you should see the Server API listed as “FPM/FastCGI”.

nginx-fcgi-3

Additional Configuration

Congratulations! You have a basic nginx and PHP install set up and serving files! But there are a few additional configuration steps that I recommend.

Web Root Permissions

A quick check of the web root’s permissions shows that it’s not writable by anyone other than root. Constant sudo-ing grows tiresome, and tinkering around as root is generally a bad idea, so I recommend executing the following:

sudo adduser <username> www-data
sudo chgrp -R www-data /usr/share/nginx/html
sudo chmod -R g+rw /usr/share/nginx/html
sudo chmod g+s /usr/share/nginx/html

adduser adds your username (replace <username> with your own login) to the www-data group, the same group that nginx runs under in the default Ubuntu install. chgrp recursively updates the html directory and its children to belong to the www-data group.

The first chmod command then grants read and write group privileges recursively to html and its children, and the second sets the SGID bit so that any files or directories created in html will take on the www-data group as its group owner. Note that the second chmod is not run recursively.

After the four commands have been run, you’ll need to reload your shell for the group association on your user account to take effect. Log out and then back in again. Once you’re in, you’ll be able to create, edit, and delete files in the web root to your heart’s content under your standard login without any escalated privileges.

Non-existent Scripts

I also recommend adding a try_files directive somewhere in the location block you uncommented earlier in the configuration, like this:

location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    try_files $uri $uri/ =404;
...

This protects you from a known vulnerability that results from an incorrectly configured system where nginx tries various patterns to satisfy a request. You may want to tweak the exact list of values after reading the documentation for try_files and take into consideration the needs of your application, but the above should provide a minimum level of protection.

Porting .htaccess Files

At some point you’ll want to serve an existing PHP application that was previously served by Apache, and most likely it will have some configuration details for the server either in an .htaccess file or in Apache’s static configuration files (mod_rewrite directives, for example). There are a couple online converters that “translate” Apache directives to nginx, and these are a good starting point in porting your configuration. But be careful not to rely on them entirely. As the saying goes, “Pasting code from the Internet into production is like chewing gum found in the street.” Familiarize yourself with nginx’s documentation and double check the converter’s output to make sure everything is in order.

Conclusion

The traditional way of running PHP (Apache and mod_php) is stable and mature, and for the majority of applications is an excellent platform. But the landscape is changing, and more performant solutions like nginx are gaining widespread acceptance, even eating into Apache’s market share. In this article you saw how to configure PHP to run behind nginx with FastCGI. And with your shiny new installations ready to server your own high performance website to the masses, I wish you success!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Andrew

    Hi, thanks for this blog post, however you should really check what’s up with fastcgi_split_path_info and fastcgi_index in your specific location (“location ~ .php$”). Please refer to http://nginx.org/en/docs/http/ngx_http_core_module.html#location and http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html for the directives’ descriptions. Basically your regex location already limits what could be in the requests, so using different regex inside wouldn’t really work.

  • Dave

    Does anyone actually use Ubuntu for a web server? I use it for my local dev environment, but I was under the impression the majority of web servers use Cent OS. Still, I guess the process is going to be pretty similar.

    Personally I build from source, but I can understand why you didn’t cover that. With all the dependencies and dependencies of dependencies and dependencies of dependencies of dependencies (etc.) that need building for a reasonable PHP installation, the article would be rather long.

  • Anonymous

    Amazon offers Ubuntu images. And if you substitute Debian for Ubuntu you get a large percentage of web servers. Ubuntu is based on Debian, and in the server realm will be quite similar for most things.

  • Aaron

    I don’t see where you configured or started the fast_cgi server. Ubuntu doesn’t autostart processes upon package install, does it?

  • Van Wilson

    Thanks for the tutorial.

    I had to make my “location” section look like this on Ubuntu 12.04, because PHP wasn’t listening on a socket:

    # # With php5-cgi alone:
    fastcgi_pass 127.0.0.1:9000;
    # # With php5-fpm:
    # fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    Also, the default web directory for the nginx package on that system is now:
    /usr/share/nginx/www

    (Thought this might help others who may be confused.)

  • Patrickz

    It will be perfect if Varnish before nginx

  • Antonis Adamakos

    Yes, many sites are on Ububtu :) For easier installation you can have a look at TuxLite – http://tuxlite.com/
    For CentoOs, please look at http://centminmod.com/