If you want to start developing PHP applications, or merely work on your PHP-based site off-line, on Mac OS X you can easily do so. In this how-to we'll see how you can set up NginX, a high performance web server, with the PHP version shipped with Mac OS X itself to create a local web server. In case you're wondering, you can of course use it in parallel with MAMP, XAMPP or even the multi-PHP version server I've described in an earlier post.

Install HomeBrew

If you don't have HomeBrew already you need to install it per its instructions. HomeBrew is a package manager for Mac OS X which lets you install a lot of software without hunting around the Internet for a suitable package file.

Install NginX

Open a Terminal window and enter the following command:

brew install nginx

After a couple of minutes it should tell you that the installation is complete. Now we need to create a few links to let us easily configure NginX and start it automatically.

You can configure NginX by editing the files inside the /usr/local/etc/nginx directory.

First, we need to edit the /etc/nginx/nginx.conf file because the default is a bit problematic. From a Terminal window type

sudo open /usr/local/etc/nginx/nginx.conf -a TextEdit

and replace the contents of the file with

user yourusername staff;
worker_processes 1;
error_log /var/log/nginx.error.log;
error_log /var/log/nginx.error.log notice;
error_log /var/log/nginx.error.log info;
pid /var/tmp/nginx.pid;

events {
  worker_connections 128;
}

http {
	include 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 main;
	sendfile on;
	#tcp_nopush on;
	#keepalive_timeout 0;
	keepalive_timeout 65;
	#gzip on;

	server {
		listen 8080 default_server;
  		listen [::]:8080 default_server ipv6only=on;

		server_name localhost;
  		root /Users/yourusername/Sites;

		#access_log /var/log/nginx.localhost.access.log main;

	location / {
    		index index.html index.htm index.php;
  		}

		#error_page 404 /404.html;
  		# redirect server error pages to the static page /50x.html
		error_page 500 502 503 504 /50x.html;
		location = /50x.html {
    		root html;
  		}

		# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
		location ~ \.php$ {
			try_files $uri =404;
			fastcgi_split_path_info ^(.+\.php)(/.+)$;
			fastcgi_pass 127.0.0.1:9000;
			fastcgi_index index.php;
			fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
			fastcgi_buffers 256 128k;
			fastcgi_connect_timeout 300s;
			fastcgi_send_timeout 300s;
			fastcgi_read_timeout 300s;
			include fastcgi_params;
	  	}

		# deny access to .htaccess files, if Apache's document root
  		# concurs with nginx's one
  		location ~ /\.ht {
   			deny all;
  		}
	}

	include servers/*;
}

Change yourusername (shown in bold red letters) with your Mac OS X username.  If you don't know what it is type the following in a Terminal window

whoami

The system will reply with your username.

At this point it should be noted that the web server root is set up to be the Sites folder inside your user's directory. If you are not sure whether you have this folder, type the following in a Terminal window

mkdir /Users/`whoami`/Sites

Starting, stopping and reloading NginX

We need to first tell Mac OS X to automatically start NginX whenever we boot the computer.

Please note that the 1.8.0 you see in the following instructions is the version number of NginX I installed at the time of this writing. Looks inside /usr/local/Cellar/nginx to see what is your actual version. Just open Finder, press ⇧⌘G type /usr/local/Cellar/nginx and press ⏎.

Assuming that the version is 1.8.0, you need to type the following into a Terminal window

sudo cp /usr/local/Cellar/nginx/1.8.0/homebrew.mxcl.nginx.plist /Library/LaunchAgents/
sudo chown root:wheel /Library/LaunchAgents/homebrew.mxcl.nginx.plist

The very first time you install NginX you need to start it manually. You can do that by typing the following into a Terminal window

sudo launchctl load -w /Library/LaunchAgents/homebrew.mxcl.nginx.plist

Stopping NginX requires typing the following into a Terminal window

sudo launchctl unload -w /Library/LaunchAgents/homebrew.mxcl.nginx.plist

You can restart it using the same command you used to start it the very first time.

After editing NginX' configuration, or the configuration of one of the server blocks (sites) you need to tell NginX to reload the configuration. You can do that by typing the following into a Terminal window

sudo nginx -s reload

Set up PHP-FPM

At this point your NginX server isn't very useful. It can only serve static content. We need to tell it how to run PHP scripts. Unlike Apache, there is no PHP module for NginX. Instead, it uses PHP-FPM (FastCGI Process Manager) which is just as fast and much more secure. We just need to enable it on your Mac. Type the following in a Terminal window

sudo mkdir -p /usr/share/php/var/log
sudo mkdir -p /usr/share/php/var/run
sudo touch /usr/share/php/var/run/php-fpm.pid
sudo touch /usr/share/php/var/log/php-fpm.log
sudo chmod 0777 /usr/share/php/var/run/php-fpm.pid
sudo chmod 0777 /usr/share/php/var/log/php-fpm.log
sudo cp /private/etc/php-fpm.conf.default /private/etc/php-fpm.conf
sudo cp /etc/php.ini.default /etc/php.ini

You can edit /etc/php.ini to taste. Please note that since this is a system file you need to open it for editing from a Terminal window by typing

sudo open /etc/php.ini -a TextEdit

Now we need to edit the file /private/etc/php-fpm.conf and make some changes. Start by opening the file for editing by typing this to a Terminal window

sudo open /private/etc/php-fpm.conf -a TextEdit

Find the lines starting with pid = and error_log = and change them to

pid = /usr/share/php/var/run/php-fpm.pid
error_log = /usr/share/php/var/log/php-fpm.log

Next up, find the user = and group = lines and change them to read

user = yourusername
group = staff

Remember to change yourusername to your actual username, the same way you did for nginx.conf.

Finally, paste the following line at the bottom of the file

php_admin_value[session.save_path] = /usr/share/php/var/session

Now let's tell Mac OS X to load PHP-FPM automatically. From a Terminal window enter

sudo touch /Library/LaunchDaemons/net.php.php-fpm.plist

sudo open /Library/LaunchDaemons/net.php.php-fpm.plist -a TextEdit

Paste the following contents into this file:

<?xml version="1.0" encoding="UTF-8" ?>
<!--DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>KeepAlive</key>
 <false/>
 <key>Label</key>
 <string>net.php.php-fpm</string>
 <key>ProgramArguments</key>
 <array>
 /usr/sbin/php-fpm
 <string>--fpm-config</string>
 <string>/private/etc/php-fpm.conf</string>
 </array>
 <key>RunAtLoad</key>
 <true/>
 <key>UserName</key>
 <string>root</string>
 <key>WorkingDirectory</key>
 /usr/share/php/fpm
 <key>StandardErrorPath</key>
 <string>/var/log/system.log</string>
 <key>ServiceDescription</key>
 PHP FastCGI Process Manager
</dict>
</plist>

Now launch PHP-FPM with the following Terminal command

sudo launchctl load -F /Library/LaunchDaemons/net.php.php-fpm.plist

You only need to do that the very first time. From now on PHP-FPM will auto-start on boot.

Testing it all works

Create a new file called phpinfo.php inside the Sites directory under your user directory. A simple way to do that:

touch /Users/`whoami`/Sites/phpinfo
open /Users/`whoami`/Sites/phpinfo -a TextEdit

and enter the following contents:

<?php phpinfo();

Now visit this file from your browser at http://localhost:8080/phpinfo.php  If all went according to the plan you should see a page stating the PHP version installed on your Mac along with its configuration info. Swell!

Creating server blocks (virtual hosts)

Doing development on subdirectories is very problematic if you intend to move your site into the domain's root later on. My personal favorite is creating one subdomain per site I'm working on locally using the non-existent local.web domain name to make sure I don't accidentally screw up a live site.

Let me show you how simple it is to create a new server block (virtual host) in NginX. In our example we'll create a new site which will be accessible under http://example.local.web:8080

First we need to create a new file with a .conf extension inside the /usr/local/etc/nginx/servers directory. There is no requirement for the file name but it's a good convention to use the site's subdomain and domain name. It will help you 5-6 months later when you won't remember what you've set up, why and how. So let's name our file example.local.web.conf. From a Terminal window:

touch /usr/local/etc/nginx/servers/example.local.web.conf
open touch /usr/local/etc/nginx/servers/example.local.web.conf -a TextEdit

Paste the following contents into the file:

server {
	listen 8080;
	server_name example.local.web;
	server_name_in_redirect off;
 
	root /Users/yourusername/Sites/example;

	# Load an nginx.conf file in the site's root - FOR DEVELOPMENT ONLY!
	# IMPORTANT: If the file doesn't exist your server won't start!
	include /Users/yourusername/Sites/example/nginx.conf;

	location ~ \.php$ {
		try_files $uri =404;
		fastcgi_split_path_info ^(.+\.php)(/.+)$;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_buffers 256 128k;
		fastcgi_connect_timeout 300s;
		fastcgi_send_timeout 300s;
		fastcgi_read_timeout 300s;
		include fastcgi_params;
	}
}

The things you need to change are:

  • example.local.web. This is the name of your subdomain. It can be anything you want.
  • yourusername. Replace it with your username as we already did when setting up NginX and PHP-FPM
  • example. This is the subdirectory inside the Sites folder where the site files live, i.e. the site's root. The convention I use is that the directory name must be the same as the sudomain it's serving. Don't make yourself think!

The thing is that Mac OS X currently doesn't know that the domain example.local.web should be served by our local server. We can edit the file /etc/hosts and tell it so. From a Terminal window:

sudo open /etc/hosts -a TextEdit

Add the following line at the end of the file

127.0.0.1   example.local.web

Finally, we need to tell NginX to reload the configuration so it knows how to serve our site. From a Terminal window:

sudo nginx -s reload

That's it! You can now access your dev site from a browser typing http://example.local.web:8080/

Please note that it's best to close and re-open your browser, otherwise the browser thread may have not picked up the change in the /etc/hosts file and reply that the server was not found.

What if I don't like port 8080?

No problem at all. Just change the line

listen 8080;

in your NginX configuration files to reflect the port you want it to listen to. For example, to make it listen to the default HTTP port (which means that you can access the server as http://localhost instead of  http://localhost:8080) change the line to

listen 80;

What about SEF URLs (Joomla!), permalinks (WordPress), routes (Drupal) and so on?

This is something you need to add to your server block files, i.e. the files inside the /usr/local/etc/nginx/servers directory. Typically it's something like this:

location / {
	try_files $uri $uri/ /index.php?$args;
}

and MUST be placed BEFORE the location ~ .php$ line. That said it's best to consult the NginX and your site script's documentation for the "right way" to set it up for use with NginX. Some quick and useful links:

That's all folks! Enjoy your high performance local server!

4 comments

  • Hi. I followed your blog post from "Set up PHP-FPM" because I have already Nginx 1.9 installed throw MacPorts and running well, virtual server included but without PHP !!
    I followed your steps but when I launched " launchctl load -F /Library/LaunchDaemons/net.php.php-fpm.plist " (I'm on root) I got the response: /Library/LaunchDaemons/net.php.php-fpm.plist: Invalid property list
    FYI, # php-fpm -v already answers: PHP 5.5.27 (fpm-fcgi) (built: Jul 23 2015 00:22:14)
    Copyright (c) 1997-2015 The PHP Group
    Zend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies
    And I'm on Yosemite OS X.
    So what's wrong?
    Thanks.
  • Hi, I could not get past this point:

    sudo launchctl load -F /Library/LaunchDaemons/net.php.php-fpm.plist


    I had to fix the XML of the plist file like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
    	"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    	<dict>
     		<key>KeepAlive</key>
     		<false/>
     		<key>Label</key>
    		<string>net.php.php-fpm</string>
     		<key>ProgramArguments</key>
     		<array>
     			<string>/usr/sbin/php-fpm</string>
    	 		<string>--fpm-config</string>
    	 		<string>/private/etc/php-fpm.conf</string>
     		</array>
     		<key>RunAtLoad</key>
     		<true/>
     		<key>UserName</key>
     		<string>root</string>
     		<key>WorkingDirectory</key>
     		<string>/usr/share/php/fpm</string>
     		<key>StandardErrorPath</key>
     		<string>/var/log/system.log</string>
     		<key>ServiceDescription</key>
     		<string>PHP FastCGI Process Manager</string>
    	</dict>
    </plist>