Automated multihosting with Nginx for PHP with FastCGI

Nginx configuration for this:

  • if client request does not have x_forwarded_for, deny the request showing a 404
  • allow access site even if x_forwarded_for:
  • if “Host” header is a IP use /var/www/vhosts/ip as root dir
  • if “Host” header is a domain use /var/www/vhosts/$domain as root dir
  • if “Host” is sub0.sub1.oranything.domain still goes to /home/vhosts/$domain
  • backend for PHP fastcgi
  • if requested file doesnt exist throw 404 error
  • setting the default index page

Strongly based on

server {

server_name default_server;

# Keep a root path in the server level, this will help automatically fill
# Information for stuff like FastCGI Parameters
root /var/www/vhosts/;

# You can set access and error logs at http, server and location level
access_log /var/log/nginx/server.access.log;
error_log /var/log/nginx/server.error.log debug;

# if client request does not have x_forwarded_for, deny the request,
if ($http_x_forwarded_for = "" ) { set $x 1; }
# .. except if host is|
if ($host ~* "(www\.public1\.com|www\.public2\.com)" ) { set $x 0; }
# .. or if uri is publicdir
if ($uri ~* "(publicdir)" ) { set $x 0; }
# .. evaluating $x
if ($x = 1) { return 404; }

# if Host is a IP vhost is "ip"
if ($host ~* "(\d+)\.(\d+)\.(\d+)\.(\d+)" ) { set $vhost "ip"; }

# if Host is a DN vhost is the vhost
if ($host ~* "(\w+\.)*(\w+)\.([a-z]+)" ) { set $vhost "$2.$3"; }

# If file doesnt exist: Error 404
if (!-e $document_root/$vhost/$uri) { return 403; }

# Setting the index page
set $i "/index.html";
if (-e $document_root/$vhost/index.php) { set $i "/index.php"; }
if (-e $document_root/$vhost/index.htm) { set $i "/index.htm"; }
if ($uri ~* "(.*)/") {
rewrite ^(.*)(/)$ $1$i redirect;
if (-d $document_root/$vhost/$uri) {
rewrite ^(.*)$ $1$i redirect;

# It will try for static file, folder, then falls back to index.php
# Assuming index.php is capable of parsing the URI automatically
location / {
try_files /$vhost$uri /$vhost/index.php ;

# Prevent ".." navigations: Error 403
location ~ \..*/.*\.php$ {
return 403;

# This block will catch static file requests, such as images, css, js
# The ?: prefix is a 'non-capturing' mark, meaning we do not require
# the pattern to be captured into $1 which should help improve performance
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
# Some basic cache-control for static files to be sent to the browser
expires max;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";

# PHP location block and parameters
location ~ \.php {
if (!-e $document_root/$vhost/$uri) { return 404; }
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root/$vhost/$fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
#fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_ADDR $remote_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param HTTP_HOST $host;

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 8 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

# I use a socket for php, tends to be faster
# for TCP just use
# fastcgi_pass unix:/opt/php-fpm.sock;

# Not normally needed for wordpress since you are
# sending everything to index.php in try_files
# this tells it to use index.php when the url
# ends in a trailing slash such as
fastcgi_index index.php;

# Most sites won't have configured favicon or robots.txt
# and since its always grabbed, turn it off in access log
# and turn off it's not-found error in the error log
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

# Rather than just denying .ht* in the config, why not deny
# access to all .invisible files
location ~ /\. { deny all; access_log off; log_not_found off; }

Proxying with Nginx

nginx logo

Today, something refer to Nginx and proxy rules. The tips showed bellow aren’t a advanced rules but should be valid for 80% of all cases that we should want to us.

server {
  listen   8008;
  access_log  /var/log/nginx/;

  location /reflector/mcast {
    include /etc/nginx/include-enabled/proxy_settings.conf;
    # Other settings related to proxy: headers, timeouts ...
    rewrite  ^/reflector/mcast/8$ redirect;
    rewrite  ^/reflector/mcast/12$  /video.php?channel=12  break;

The /etc/nginx/include-enabled/proxy_settings.conf would be something like this:

proxy_set_header  X-Real-IP  $remote_addr;
proxy_set_header  X-Forwarded-For  $remote_addr;
proxy_read_timeout 300;

The first rewrite rule defines a 302 redirection behavior. The server respond to client a 302 response refer to The second one, respond a 20X response. Petition will be rewrite to /video.php?… and it’ll be delegate to proxy_pass module. In this case, the server implement a reverse proxy behavior.
Observe next items:

  • All petitions like /videos/ are proxified to
    location /videos/ {
      include /etc/nginx/include-enabled/proxy_settings.conf;
  • All petitions like /media/video.mpeg are proxified to
    location /media/ {
      include /etc/nginx/include-enabled/proxy_settings.conf;
      rewrite  ^/media/(.*)$ /videos/$1  break;
  • All petitions like /media/8 are proxified to
    location /media/ {
      include /etc/nginx/include-enabled/proxy_settings.conf;
      rewrite  ^/media/(.*)$ /?video=$1  break;

Finally, next rule uses $arg_ special var to refer the get parameters in the URI. In this case, this server expect URLs like “/play?mo=video.mpeg” and it’ll try to serve video.mpeg storaged on /var/www.

location /play {
  root /var/www/;
  rewrite ^/play /$arg_mo redirect;

Enjoy it!.