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; }

Django Installation & Configuration using Nginx+FastCGI+PosgtreSQL

Django box

Configuring PostgreSQL

  1. Install PostgreSQL
  2. Create a django user

    sudo -u postgres createuser -P django_user

    Also you can alter the user attributes as follow:

    sudo su -
      passwd postgres
      su postgres
      psql template1
      ALTER USER django_user WITH ENCRYPTED PASSWORD 'mypassword';
  3. Create the Django project database::
    sudo -u postgres psql template1
    CREATE DATABASE django_db OWNER django_user ENCODING ‘UTF8’;
  4. Give comoda access (pg_hba.conf):
  5. local   django_db        django_user                      md5

Configuring access to PostgreSQL

  1. Install psycopg2 Python package.
  2. Verify the installation

    You should be all set now, but let’s verify this right away. Open the
    shell and run the following instructions inside the python shell (start
    off with the python command):

    >>> import django
    >>> print django.VERSION
    (0, 97, 'pre')
    >>> import psycopg2
    >>> psycopg2.apilevel
  3. Configure Django project settings ( on the project directory):
    DATABASE_ENGINE = 'postgresql_psycopg2'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
    DATABASE_NAME = 'django_db'             # Or path to database file if using sqlite3.
    DATABASE_USER = 'django_user'             # Not used with sqlite3.
    DATABASE_PASSWORD = 'XXXXXX'         # Not used with sqlite3.
    DATABASE_HOST = 'localhost'             # Set to empty string for localhost. Not used with sqlite3.
    DATABASE_PORT = '5432'             # Set to empty string for default. Not used with sqlite3.
  4. Install Django Python package.
  5. Run syncdb from Django project directory:
    python syncdb

Running Django project like FastCGI

  1. Install Django and Flup Python packages.
  2. You need also to start Django fastcgi server (from the project folder):
    python runfcgi host= port=8000 --settings=settings

    If you need to add something to pythonpath:

    python runfcgi host= port=8000 --settings=settings --pythonpath=/a/path/to/somewhere/

Configuring Nginx to serve the Django FastCGI service

  1. Configure Nginx:
    server {
    listen   80;
    #server_name  localhost;
    location / {
    root   html;
    rewrite  ^$request_uri  redirect;
    access_log  /var/log/nginx/localhost.access.log;
    server {
    listen   443;
    # server_name  localhost;
    access_log  /var/log/nginx/localhost-ssl.access.log;
    ssl_prefer_server_ciphers   on;
    ssl  on;
    ssl_certificate      /etc/nginx/ssl/all_com.crt;
    ssl_certificate_key  /etc/nginx/ssl/all_com.key;
    ssl_session_timeout  5m;
    #ssl_protocols  SSLv2 SSLv3 TLSv1;
    #ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    location / {
    # host and port to fastcgi server
    fastcgi_param PATH_INFO $fastcgi_script_name;
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;
    fastcgi_pass_header Authorization;
    fastcgi_intercept_errors off;
    #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   /var/www/nginx-default;
  2. Access to http://<django-server>/.