Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Setting up a FreeBSD Server on Hetzner, Part 5: PHP, SSI, SSL, Redirects

In this blog post we describe the procedure to configure nginx on a FreeBSD VM to use PHP, SSI (Server Side Includes), SSL, and redirects.

We will configure the following server blocks:

  • nono.com
  • nono.com (SSL)
  • www.nono.com (301 permanent redirect to nono.com)

What we want our website to look like

what our website should look like (redirect, SSI, SSL,

Our final website should look like this: notice the valid SSL cert, the PHP-supplied image and IP address, and the Server Side Includes (the black boxes)

What our website actually looks like

our website without redirects, SSL, SSI, PHP

No redirects, no SSL, no SSI, no PHP

Server Side Includes

We edit nginx.conf (see final version here):

sudo -E vim /usr/local/etc/nginx/nginx.conf

We add the following line to the http stanza:

ssi on;

We save the file and restart nginx:

sudo /usr/local/etc/rc.d/nginx restart

We view the website in our browser to make sure that the SSIs have been honored (in this case, a navbar at the top with home and about links).

Make sure we have flushed our browser’s cache (otherwise we may end up looking at a cached version of the website and falsely assume that our changes failed). The Firefox keyboard shortcut to override the cache and refresh a page on OS X is ⇧⌘R (Shift-Command-R)

Our website now properly inlines the included files; however, the PHP portions are still broken (no picture in the middle from on top, no IP Address listed on the top left next to “Your IP”)

website with SSI

nginx with SSI configured. Although the black boxes appear, the PHP-supplied content is still missing (e.g. image, IP address)

PHP

Installing PHP under nginx is a bit of a slog compared to installing it under Apache (uncomment the appropriate loadmodule, add the .php extension, and restart):

Install PHP-FPM

First, we need to install FreeBSD’s ports collection, which is FreeBSD’s counterpart to OS X’s homebrew. We only need to do this step if ports isn’t installed (i.e. if the directory /usr/ports doesn’t exist).

sudo portsnap fetch
sudo portsnap extract

Next, we install PHP via the ports collection:

cd /usr/ports/lang/php5
sudo make install

When prompted, go with the defaults (the important option is FPM). There will be a chain of dependencies, some of which (e.g. m4, gmake) will prompt you to select options. Again, go with the defaults.

Let’s configure PHP FPM to start on boot and then start it up:

echo 'php_fpm_enable="YES"' | sudo tee -a /etc/rc.conf
sudo /usr/local/etc/rc.d/php-fpm start

Let’s make sure it’s running:

netstat -an | grep 9000

You should see a line similar to this:

tcp4       0      0 127.0.0.1.9000         *.*                    LISTEN

Configure nginx to use PHP-FPM

Let’s edit nginx.conf:

sudo -E /usr/local/etc/nginx/nginx.conf

We add the following line to the server stanza:

    location ~ .php$ {
      fastcgi_pass 127.0.0.1:9000;
    }

We save the file and restart nginx:

sudo /usr/local/etc/rc.d/nginx restart

We create a test PHP file to make sure that PHP is running properly:

echo "<?php phpinfo(); ?>" > /www/nono.com/phpinfo.php

We browse to our test URI http://shay.nono.com/phpinfo.php. We notice that instead of seeing the PHP configuration information, we see a blank page. PHP is broken. We check the nginx log files in /var/www, but don’t see anything useful. We need to discover where PHP-FPM is logging. To do that, we install and run lsof (a utility which prints out the open filehandles of the processes on a system, a useful technique to discover where a process’s output is going):

sudo pkg_add -r lsof
lsof | grep php-fpm

In the output, we discover the location of the log files (/var/log/php-fpm.log), which, in retrospect, is an obvious location to look for a log file:

php-fpm 81062   root    2w    VREG               0,73                116 1205900 /var/log/php-fpm.log
php-fpm 81062   root    3w    VREG               0,73                116 1205900 /var/log/php-fpm.log

We look at the log file (sudo less /var/log/php-fpm.log). We see nothing but the usual startup messages.

Let’s make sure PHP is configured correctly by running a snippet of PHP code: php -r "phpinfo();". Sure enough, it gives the expected output (i.e. a description of the PHP environment information).

PHP seems to be working properly, so let’s turn our attention to PHP-FPM. First, the basics: man php-fpm.

We notice it has a configuration file; let’s examine it: less /usr/local/etc/php-fpm.conf.

After reviewing the configuration file, we see it has two directives that we can use to our advantage to troubleshoot the problem:

sudo vim /usr/local/etc/php-fpm.conf # change log_level and run in foreground
  log_level = debug
  ...
  daemonize = no
sudo /usr/local/etc/rc.d/php-fpm restart

We browse to our site (http://shay.nono.com/phpinfo.php), still a blank page. And the output from our terminal session tells us nothing. We revert our changes.

In a fit of desperation, we sniff the traffic on port 9000 to see what’s being passed to the PHP-FPM daemon. We start our tcpdump session:

sudo tcpdump -A -ni lo0 port 9000

Then we browse again to our site (http://shay.nono.com/phpinfo.php). We see from the output of tcpdump that nginx is not passing the name of the .php file it needs to execute.

Let’s modify our nginx.conf, add the line to pass the CGI script, and restart the nginx daemon (our current version of our nginx.conf can be seen here)

sudo -E vim /usr/local/etc/nginx.conf # add the following lines
  fastcgi_param SCRIPT_FILENAME /www/nono.com$fastcgi_script_name;
  include fastcgi_params;
sudo /usr/local/etc/rc.d/nginx restart

We browse again to our site (http://shay.nono.com/). It works! Our output is beautiful:

web page with working PHP

nginx now properly executes PHP (note the image and the IP address)

Redirects

Adding a redirect is simple—we add the following line to nginx.conf:

  rewrite ^ https://nono.com$request_uri?;

We restart the nginx daemon, and again browse to our site (http://shay.nono.com/). And we are redirected, but to our old site, our original site, which works fine.

This is not helpful. Rather than being redirected to our old site (i.e. the ARP Networks site), we would rather be redirected to the new site, in Germany (i.e. the Hetzner site). But how can we accomplish this? We want nono.com to resolve to shay.nono.com’s IP address (i.e. 78.47.249.19), but only for our local machine, only until we have finished the migration and are satisfied with the results.

The /etc/hosts override

We are going to use that much-maligned hack, the /etc/hosts override [1] (“universally used, universally despised” is how we characterize it). We edit our /etc/hosts file on our local machine (in this particular case, a 2012 Mac Mini) and add the following line:

78.47.249.19    nono.com

This line has the effect of saying, “I don’t care what the Internet at large thinks the IP address of nono.com, as far as I’m concerned it’s 78.47.249.19″.

We refresh our browser, and we see this message, “Unable to connect. Firefox can’t establish a connection to the server at nono.com.” Our override is working properly because we have never configured the SSL portion of nginx.

SSL

We edit our nginx.conf file, and add the following lines:

    server {
      server_name nono.com;
      listen              443 ssl;
      ssl_certificate     nono.com.chained.crt;
      ssl_certificate_key nono.com.key;
      access_log /var/www/nono.com-access.log;
      error_log /var/www/nono.com-error.log;
      root /www/nono.com;
      index index.shtml index.html index.htm;
      ssi on;
      location ~ .php$ {
        ssi on;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME /www/nono.com$fastcgi_script_name;
        include fastcgi_params;
      }
    }

We also copy our keys into place:

sudo cp nono.com.key nono.com.chained.crt /usr/local/etc/nginx
sudo chown root:wheel /usr/local/etc/nginx/{nono.com.key,nono.com.chained.crt}

We go to extra lengths to protect our .key file, ensuring that only the owner can read it and protect us from accidentally checking it into our public repo.

sudo chmod 400 /usr/local/etc/nginx/nono.com.key
echo '*.key' | sudo tee -a /usr/local/etc/.gitignore

We restart our server:

sudo /usr/local/etc/rc.d/nginx restart

And see the following error message:

nginx: [emerg] the "ssl" parameter requires ngx_http_ssl_module in /usr/local/etc/nginx/nginx.conf:56

We have made a mistake. We installed the stock version of nginx, but it lacks the ngx_http_ssl module, which “is not built by default“. We need to install a custom version, we need to install via ports.

First we uninstall the stock nginx:

sudo pkg_info | grep nginx # look for the exact nginx package name
sudo pkg_delete nginx-1.4.2,1

Let’s install nginx from the ports collection. When presented with the configuration screen, remember to make sure HTTP_SSL is checked. It should be; it’s the default.

cd /usr/ports/www/nginx
sudo make install

When it has finished installing, we can attempt to start it up again:

sudo /usr/local/etc/rc.d/nginx restart

And browse again to http://shay.nono.com. Success! Our website looks the way we want it to look:

what our website should look like (redirect, SSI, SSL,

Our final website should look like this: notice the valid SSL cert, the PHP-supplied image and IP address, and the Server Side Includes (the black boxes)

IPv6

To configure nginx to listen on both IPv4 and IPv6, we need to add a second listen directive for IPv6 ([::]:80;). Here’s a snippet from our nginx.conf:


      server_name _; # invalid value which will never trigger on a real hostname.
      listen 80;
      listen [::]:80;

Acknowledgements

Stan and Moe have a good post on configure PHP on nginx under FreeBSD.

The nginx site has the canonical instructions for configuring SSL under nginx.

Footnotes

1 Given a hostname, the typical UNIX (Linux, OS X, *BSD) operating system will use the gethostbyname(3) library call to determine the address, (e.g. gethostbyname(“nono.com”) will return a struct which has the IP address 78.47.249.19) (actually it will probably use the getaddrinfo(3) library call, but that’s a topic for another day). There are many levers/overrides to this library call (e.g. there’s the previously mentioned /etc/hosts override, but there is also an acknowledgement that DNS is not always the source of a host’s IP address; other alternative sources include the now-venerable Sun Microsystems’s NIS (a.k.a. “Yellow Pages”) and LDAP). The levers (often configured in a file named either “/etc/nsswitch.conf” or “/etc/hosts.conf”) can specify precedence of various sources of information, e.g. “Check the /etc/hosts file first for the IP address. If not found, check LDAP next, and if you still don’t find the host, check DNS”.


Comments
Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *