August 30, 2014

How to Install Nginx and PHP-FPM on FreeBSD

Nginx Logo

Using Nginx and PHP-FPM on FreeBSD is a very powerful combination. The web server and proxy Nginx is build for high concurrency environments. It delivers faster performance and also uses less memory. The FastCGI Process Manager (PHP-FPM) is an alternative PHP FastCGI implementation. PHP-FPM is useful for sites of any size, especially busier sites. By following these instructions you will combine the performance benefits of Nginx and PHP-FPM with the robustness of the FreeBSD system.

Root access is required to edit the following files and to execute commands. Log in as root (su) or simply prepend sudo to all commands that require root privileges.

Install Nginx

Install Nginx from the FreeBSD ports tree.

cd /usr/ports/www/nginx
make install clean; rehash

We have installed Nginx with the following options:

[X] HTTP_MODULE               Enable HTTP module
[X] HTTP_ADDITION_MODULE      Enable http_addition module
[X] HTTP_CACHE_MODULE         Enable http_cache module  
[X] HTTP_GEOIP_MODULE         Enable http_geoip module
[X] HTTP_GZIP_STATIC_MODULE   Enable http_gzip_static module
[X] HTTP_IMAGE_FILTER_MODULE  Enable http_image_filter module 
[x] HTTP_PERL_MODULE          Enable http_perl module 
[X] HTTP_REALIP_MODULE        Enable http_realip module
[X] HTTP_REWRITE_MODULE       Enable http_rewrite module  
[X] HTTP_STATUS_MODULE        Enable http_stub_status module

Install PHP with FPM

First, we need to install libtool. Libtool is a generic library support script. Libtool hides the complexity of using shared libraries behind a consistent, portable interface.

cd /usr/ports/devel/libtool
make install clean; rehash

Install PHP from the FreeBSD ports tree.

cd /usr/ports/lang/php5
make install clean; rehash

Here are our build options for PHP. Make sure that you have enabled FPM.

[X] CLI        Build CLI version
[X] CGI        Build CGI version
[X] FPM        Build FPM version (experimental)
[X] SUHOSIN    Enable Suhosin protection system

Open the rc.conf.

vi /etc/rc.conf

Make these settings in you rc.conf to load PHP and Nginx on system-start.

php_fpm_enable="YES"
nginx_enable="YES"

Configure PHP

Copy and rename the default production php.ini.

cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

Open the php.ini file.

vi /usr/local/etc/php.ini

Replace the following settings in your php.ini if necessary.

error_reporting = E_ALL | E_STRICT
cgi.fix_pathinfo=1
expose_php = Off
upload_max_filesize = 200M
post_max_size = 200M
max_execution_time = 600
max_input_time = 600
memory_limit = 256M
mysql.allow_persistent = Off
register_argc_argv = On
date.timezone = Europe/Berlin
register_globals = Off
allow_url_fopen = Off
magic_quotes_gpc = Off
magic_quotes_runtime = Off

Start PHP with FPM.

/usr/local/etc/rc.d/php-fpm start

Check that PHP has successfully started.

netstat -l

Install Maxmind GeoIP

Install the Maxmind GeoIP library.

mkdir -p /opt/conf
cd /opt/conf
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
gunzip ./GeoIP.dat.gz

Configure Nginx

Open the nginx.conf.

vi /usr/local/etc/nginx/nginx.conf

This is our nginx.conf. Please make the required changes.

user www www;
worker_processes 4;
error_log /var/log/nginx/error.log crit;
pid /var/run/nginx.pid;
events {
    worker_connections 1024;
}
http {
    geoip_country /opt/conf/GeoIP.dat;
    include /usr/local/etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log off;
    server_tokens off;
    sendfile on;
    client_max_body_size 200m;
    client_body_buffer_size 1m;
    keepalive_timeout 1;
    port_in_redirect off;
    gzip on;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript application/xml application/xml+rss text/javascript;
    gzip_buffers 16 8k;
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    include /usr/local/etc/nginx/conf.d/*.conf;
}

Now, create a new directory to host your domain specific settings. All .conf files will be recognized by Nginx. Have a look at the include line above.

mkdir /usr/local/etc/nginx/conf.d

Now we will create an example domain_com.conf file. Make sure that the correct directory ownership and permissions are set on any domain directory. Otherwise Nginx can not access the directories. In our example it is the /var/www/domain.com directory and we are using www as user and group. Create the domain directory and set directory ownership and permissions:

mkdir /var/www/domain.com
chown www:www /var/www/domain.com
chmod 755 /var/www/domain.com

Place a sample index.php file in the new directory.

sh -c 'echo "<?php phpinfo(); ?>" > /var/www/domain.com/index.php'

Create the domain configuration file:

vi /usr/local/etc/nginx/conf.d/domain_com.conf

Copy and paste the code below:

server {
        listen 80;
        server_name www.domain.com;
        rewrite ^ http://domain.com$request_uri?;
}
server {
        listen 80;
        server_name domain.com;
        server_name_in_redirect off;
        root /var/www/domain.com;
        location ~* ^.+\.(ico|js|gif|jpg|jpeg|png|bmp)$ {
          expires 30d;
        }
        location / {
            index index.php;
        }
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param GEOIP_COUNTRY_CODE $geoip_country_code;
            fastcgi_param GEOIP_COUNTRY_NAME $geoip_country_name;
            fastcgi_param SCRIPT_FILENAME /var/www/domain.com$fastcgi_script_name;
            include fastcgi_params;
        }
        location ~ /\.ht {
            deny all;
        }
}

Check that all Nginx settings are correct.

nginx -t

Install some more packages

Install this port to connect PHP with MySQL. (optionally)

cd /usr/ports/databases/php5-mysql
make install clean; rehash
pkg_info|grep php|grep mysql

Install this port to use PHP sessions. (optionally)

cd /usr/ports/www/php5-session
make install clean; rehash

Install this port to make use of the PHP GD library. (optionally)

cd /usr/ports/graphics/php5-gd
make install clean; rehash

Install this port to read the EXIF informations of images with PHP. (optionally)

cd /usr/ports/graphics/php5-exif
make install clean; rehash

Restart PHP-FPM und Nginx

Finally let us restart PHP-FPM and Nginx.

/usr/local/etc/rc.d/php-fpm restart
/usr/local/etc/rc.d/nginx restart
  • DJ

    Thank you, very usefull.

  • Peter Wright

    followed all the above and running nginx -t or restarting i get
    server# /usr/local/etc/rc.d/nginx restart
    Performing sanity check on nginx configuration:
    nginx: [emerg] unknown directive “geoip_country” in /usr/local/etc/nginx/nginx.conf:19
    nginx: configuration file /usr/local/etc/nginx/nginx.conf test failed

    line 19 > geoip_country /opt/conf/GeoIP.dat;

    • Moe

      This error indicate that your loaded binary doesn’t have geoip module compiled in.
      Try to run /usr/local/etc/rc.d/nginx upgrade or make a clean stop and start.

      Additionally make sure that you have selected
      [X] HTTP_GEOIP_MODULE Enable http_geoip module
      during the install process.

  • http://www.comal-consulting.eu Oliver

    Hi Moe,

    great post, helped me a lot as a newbie to FreeBSD, i did follow your tutorial, but on the end I got a “404 not found” when I tried to visit the page in the browser, the error.log told me that it can’t find the index.html in /usr/local/etc/nginx/html/ what is true because the folder doesn’t exist.
    I had to adjust a few things in the /usr/local/etc/nginx/conf.d/domain_com.conf

    server {
            listen 80;
            server_name domain.com;
            server_name_in_redirect off;
            root /var/www/domain.com;
            location ~* ^.+.(ico|js|gif|jpg|jpeg|png|bmp)$ {
              expires 30d;
            }
            location / {
                index index.php;
            }
            location ~ .php$ {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param GEOIP_COUNTRY_CODE $geoip_country_code;
                fastcgi_param GEOIP_COUNTRY_NAME $geoip_country_name;
                fastcgi_param SCRIPT_FILENAME /var/www/domain.com$fastcgi_script_name;
                include fastcgi_params;
            }
            location ~ /.ht {
                deny all;
            }
    }
    

    I took out the root from the locations and placed it only one time before.
    According to http://wiki.nginx.org/Pitfalls#Root_inside_Location_Block it is recommended.

    How ever, thanks a lot for your great tutorial.
    Cheers
    Oliver

    • Moe

      Hi Oliver,

      good to know that you could benefit from this tutorial.
      Yes, you are right about the missing domain directory and index.php file.
      I have included the steps to create the directory and file and placed root out of the location block as well.

      Thanks for the note.
      Moe

  • Tomas Hastings

    Thanks for the tutorial, I’d never used nginx before and have it running like a charm with GeoIP data now!

    But there is one little flaw in your setup, you need to modify it a little to prevent execution of php code from uploaded files.

    Read up on the exploit here: http://forum.nginx.org/read.php?2,88845,page=3

    Also, why not change this:

    cd /
    mkdir opt
    cd opt
    mkdir conf
    cd conf
    

    To this:

    mkdir -p /opt/conf
    

    and save 5 seconds

    • Moe

      Hi Tomas,

      thanks for the tip. Since PHP 5.3.9+ this problem seems to be solved but I will read more about it and change this tutorial if needed. As I read out from the Nginx forum posts a quick work-around would be adding the following line to the php.ini:

      cgi.fix_pathinfo = 0
      

      An other solution would be adding this block in the Nginx configuration:

      if ( $fastcgi_script_name ~ ..*/.*php ) {
          return 403;
      }
      

      I have replaced the /opt/conf directory creation with your proposal as well.

      Regards,
      Moe

  • Dale

    Is there a way to not have to specify that block of PHP for each server?

    location ~ .php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    include fastcgi_params;
    }

  • Claus

    Thanks, this was very useful. I strayed from the tutorial and tried to choose the DTRACE option for PHP-FPM, in addition to those mentioned. Didn’t work, so I ran “make rmconfig” and then “make install clean” again. The rehash command isn’t available on my machine, but it didn’t seem to matter.