Nginx Logo

Software version1.2.1
Operating SystemDebian 7
WebsiteNginx Website
Last Update29/08/2014

Introduction

Nginx [engine x] is a web server (or HTTP) software written by Igor Sysoev, whose development began in 2002 for the needs of a high-traffic Russian site.

I’ve been looking for an alternative to Apache for some time because it’s too resource-hungry. When discovering lighttpd, I realized that other servers existed besides Apache and IIS. It was time to dig deeper into this question.

Installation

For the installation on Debian, it’s always simple:

  aptitude install nginx
  

Then we’ll start it:

  /etc/init.d/nginx start
  

Configuration

php-fpm

This is the most optimal solution for multi-threaded machines (i.e., several cores).

Installation

  aptitude install php5-fpm
  

Configuration

For the configuration, we will create a basic configuration (if possible, delete the default configuration). We will change it later, but this gives you an idea of a minimal working configuration:

  server {
    listen       80;
    server_name  www.deimos.fr;
    root   /var/www/deimos.fr;
    index  index.html index.htm index.php;

    location ~ \.php$ {
        fastcgi_index  index.php;
        fastcgi_pass   unix:/var/run/php5-fpm.sock;
        # fastcgi_pass    127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
  

We will change the listening type from TCP to sockets:

  ; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /var/run/php5-fpm.sock
  

Then apply the configuration:

  ln -s /etc/nginx/sites-available/www.deimos.fr /etc/nginx/sites-enabled/www.deimos.fr
  

Then restart php-fpm and nginx!

PHP-FPM Status

If you need to monitor PHP-FPM, you’ll certainly need information about your PHP-FPM. For this, edit the following file and uncomment these lines:

  [...]
pm.status_path = /fpm-status
[...]
  

Then edit your Nginx configuration and add:

  [...]
    # PHP-FPM Status
    location /fpm-status {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        # User access
        auth_basic "Please logon";
        auth_basic_user_file /etc/nginx/access/htaccess;
    }
[...]
  

To prevent anyone from accessing it, I’ve placed a htaccess, but you can choose the security method you prefer.

Restart PHP-FPM and Nginx, then access your server like this:

  • http://server/fpm-status: general information
  • http://server/fpm-status?full: detailed general information
  • http://server/fpm-status?json&full: json export
  • http://server/fpm-status?html&full: html export
  • http://server/fpm-status?xml&full: xml export

This will give you something like this in the non-detailed version:

  pool:                 www
process manager:      dynamic
start time:           18/Dec/2013:19:00:41 +0100
start since:          52972
accepted conn:        5268
listen queue:         0
max listen queue:     0
listen queue len:     0
idle processes:       3
active processes:     1
total processes:      4
max active processes: 4
max children reached: 0
  

PHP Fast CGI

This method works but is not optimal. Use php-fpm if you can.

Installation

To make Nginx support PHP, we’ll install:

  aptitude install php5-cgi
  

Then we will create an init service for this fast cgi:

  #! /bin/sh
### BEGIN INIT INFO
# Provides:          php-fcgi
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start and stop php-cgi in external FASTCGI mode
# Description:       Start and stop php-cgi in external FASTCGI mode
### END INIT INFO

# Author: Kurt Zankl <kz@xon.uni.cc>
# Modified by Deimos <xxx@mycompany.com>

# Do NOT "set -e"

BIND=127.0.0.1:9000
USER=www-data
PHP_FCGI_CHILDREN=15
PHP_FCGI_MAX_REQUESTS=1000

PATH=/usr/bin:/sbin:/usr/sbin:/bin
PHP_CGI=/usr/bin/php-cgi
PHP_CGI_NAME=`basename $PHP_CGI`
PHP_CGI_ARGS="- USER=$USER PATH=$PATH PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND"
RETVAL=0

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

do_start()
{
        echo -n "Starting PHP FastCGI : "
        start-stop-daemon --start --quiet --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS
        RETVAL=$?
        echo "$PHP_CGI_NAME."
}

do_stop()
{
        echo -n "Stopping PHP FastCGI : "
        killall -q -w -u $USER $PHP_CGI
        RETVAL="$?"
        echo "$PHP_CGI_NAME."
}

case "$1" in
   start)
      do_start
   ;;
   stop)
      do_stop
   ;;
   restart)
      do_stop
      do_start
   ;;
   *)
      echo "Usage: php-fcgi {start|stop|restart}"
      exit 3
    ;;
esac
exit $RETVAL
  

Then we will update the default RCs and start everything:

  cd /etc/init.d
chmod 755 /etc/init.d/php-fcgi
update-rc.d php-fcgi defaults
/etc/init.d/php-fcgi start
  

Configuration

To configure PHP support, edit the base Nginx file and adapt:

  server {
       listen   80;
       server_name  localhost;

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

       location / {
               root   /var/www/nginx-default;
               index  index.html index.htm index.php;
       }

       location /doc {
               root   /usr/share;
               autoindex on;
               allow 127.0.0.1;
               deny all;
       }

       location /images {
               root   /usr/share;
               autoindex on;
       }

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

       # proxy the PHP scripts to Apache listening on 127.0.0.1:80
       #
       #location ~ \.php$ {
               #proxy_pass   http://127.0.0.1;
       #}

       # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
       #
       location ~ \.php$ {
               root /var/www/nginx-default;
               include /etc/nginx/fastcgi_params;
               fastcgi_pass   127.0.0.1:9000;
               fastcgi_index  index.php;
               fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default/$fastcgi_script_name;
       }

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

nginx.conf

Here is the global server configuration. I added comments on important lines:

  user www-data;
# Number of working process
worker_processes 2;
worker_rlimit_nofile    2000;
pid /var/run/nginx.pid;

events {
    # Maximum connection number
    worker_connections 2048;
    use epoll;
    # multi_accept on;
}

http {
	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
    map_hash_bucket_size 64;
    # Security to hide version
	server_tokens off;

	server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

    # Grow this 2 values if you get 502 error message
    fastcgi_buffers 256 16k;
    fastcgi_buffer_size 32k;
    # Nginx cache to boost performances
    fastcgi_cache_path /usr/share/nginx/cache levels=1:2
        keys_zone=mycache:10m
        inactive=1h max_size=256m;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Gzip Settings
	##

	gzip on;
	gzip_disable "msie6";
        gzip_static on;
	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

	##
	# nginx-naxsi config
	##
	# Uncomment it if you installed nginx-naxsi
	##

	#include /etc/nginx/naxsi_core.rules;

	##
	# nginx-passenger config
	##
	# Uncomment it if you installed nginx-passenger
	##

	#passenger_root /usr;
	#passenger_ruby /usr/bin/ruby;

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}
  

drop.conf

This file will allow us to not log non-essential information and prevent access to potentially sensitive files:

  # Do not log robots.txt if not found
location = /robots.txt  { access_log off; log_not_found off; }
# Do not log favicon.ico if not found
location = /favicon.ico { access_log off; log_not_found off; }
# Do not give access to hidden files
location ~ /\.          { access_log off; log_not_found off; deny all; }
# Do not give access to vim backuped files
location ~ ~$           { access_log off; log_not_found off; deny all; }
  

Then in your configuration files, just add these 2 lines:

  server {
[...]
    # Drop config
    include drop.conf;
}
  

VirtualHosts

Just like with Apache, you can create virtual hosts. For example, let’s create the deimos.fr blog:

  server {
       listen   80;
       server_name  blog.deimos.fr;

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

       location / {
               root   /var/www/nginx-default/blog;
               index  index.html index.htm index.php;
       }

       location /doc {
               root   /usr/share;
               autoindex on;
               allow 127.0.0.1;
               deny all;
       }

       location /images {
               root   /usr/share;
               autoindex on;
       }
}
  

Here, blog.deimos.fr will be accessible on port 80.

Let’s activate this config:

  ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog
  

All that remains is to reload the server and it works :-)

htaccess

To restrict access to some of your sites, here’s one of the oldest but effective solutions - htaccess! To enable them, we will need the htpasswd binary:

  aptitude install apache2-utils
  

We will now generate a file containing the logins and passwords with a first user:

  htpasswd -c /etc/nginx/htaccess deimos
  

Enter the password and you’re set. Let’s declare it in the config, in my previously created virtualhost:

  server {
       listen   80;
       server_name  blog.deimos.fr;

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

       location / {
               root   /var/www/nginx-default/blog;
               index  index.html index.htm index.php;
               auth_basic "Restricted";
               auth_basic_user_file /etc/nginx/htaccess;
       }

       location /doc {
               root   /usr/share;
               autoindex on;
               allow 127.0.0.1;
               deny all;
       }

       location /images {
               root   /usr/share;
               autoindex on;
       }
}
  

All that remains is to reload the server and it works :-)

Default Port

To specify the default port, you can use an include in your configuration files that will call a file containing the port. This way it will be very easy to modify the default port in one go:

  listen 80;
  

And in the configuration files:

  server {
    include listen_port.conf;
[...]
  

SSL

Installation

For SSL installation, compared to Apache which doesn’t natively and simply allow SSL on VirtualHosts, it’s much easier with Nginx:

  aptitude install openssl
  

Configuration

First, we’ll generate SSL keys:

  mkdir -p /etc/nginx/ssl
cd /etc/nginx/ssl
openssl req -new -x509 -nodes -out server.crt -keyout server.key
  

I put it in an ssl folder with the nginx config, but since you can have multiple certificates, I encourage you to create a hierarchy:

   server {
        listen   443 ssl;
        server_name  localhost;

        ssl_certificate /etc/nginx/ssl/server.crt;
        ssl_certificate_key /etc/nginx/ssl/server.key;
        # Resumption
        ssl_session_cache shared:SSL:10m;
        # Timeout
        ssl_session_timeout 10m;

        # Security options
        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA;
        ssl_prefer_server_ciphers on;

        # HSTS (force users to come in SSL if they've already been once)
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

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

        location / {
                root   /var/www/nginx-default/webmail;
                index  index.html index.htm index.php;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
                root /var/www/nginx-default;
                include /etc/nginx/fastcgi_params;
                fastcgi_pass   unix:/var/run/php5-fpm.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default/$fastcgi_script_name;
        }
 }
  

Let’s enable this config:

  ln -s /etc/nginx/sites-available/webmail /etc/nginx/sites-enabled/webmail
  

And restart the Nginx server for it to take effect.

You can test the security of your SSL server: https://www.ssllabs.com/ssltest/index.html

Force SSL Connection

It is possible to configure your virtualhost in the normal way but to absolutely want a redirect to SSL. For this, it’s very simple, just add:

  server {
    listen      80;
    server_name www.deimos.fr;
    rewrite     ^   https://$server_name$request_uri? permanent;
}
  

FastCGI Cache

FastCGI cache is useful to increase the speed of your website. We’ll allocate a few MB for the cache and put it in a tmpfs to speed things up even more. In the main configuration, we’ll create this cache:

  [...]
http {
    [...]
    fastcgi_cache_path /usr/share/nginx/cache levels=1:2
        keys_zone=mycache:10m
        inactive=1h max_size=256m;
    [...]
}
[...]
  

Here I’m creating a cache called “mycache” with a size of 256MB.

In your VirtualHost configurations, add these lines:

  [...]
        location ~ \.php$ {
                fastcgi_cache mycache;
                fastcgi_cache_key $request_method$host$request_uri;
                fastcgi_cache_valid any 1h;
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_intercept_errors on;
        }
[...]
  

Next we’ll create a tmpfs cache folder (read this doc for more info):

  [...]
tmpfs /usr/share/nginx/cache    tmpfs   defaults,size=256m   0   0
[...]
  

Then we’ll create the cache directory, mount it and restart nginx:

  mkdir /usr/share/nginx/cache
mount /usr/share/nginx/cache
/etc/init.d/nginx restart
  

Maintenance Page

Here’s an elegant way to create a maintenance page:

  server {
[...]
   location / {
      try_files /maintenance.html $uri $uri/ @maintenance;
   }

   location @maintenance {
      return 503;
   }
}
  

All you need to do is insert a maintenance.html page at the root of your site and this page will be displayed while you make your changes. You can also use this method:

  server {
[...]
    location / {
        if (-f $document_root/maintenance.html) {
             return 503;
        }
        [...]
    }

    error_page 503 @maintenance;
    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}
  

Limit Flooding and Sniffers

It can happen that a malicious person tries to bring your server to its knees by making many requests that typically saturate PHP processes and make your CPUs go to 100%. To avoid this, it is possible to limit the number of requests per IP by using the limit_req module1. To enable this module, insert this line in your Nginx core (global) configuration:

  http {
[...]
    # Flood/DoS protection
    limit_req_zone $binary_remote_addr zone=limit:10m rate=5r/s;
    limit_req_log_level notice;
[...]
}
  

Here are the options used:

  • zone=limit: IPs will be stored in a zone called limit of 10M. Provide enough space in this zone to avoid 503 errors if there is no more space. A 1M zone can contain 16,000 binary_remote_addr type entries.
  • rate: we allow 5 requests per second.
  • log_level: you can remove this line if you don’t want to know in the logs if the limits are reached. Here I want to track this kind of event.

Next, we need to inform on which virtualhost or location we want to apply this security measure:

      location ~ \.php$ {
        limit_req zone=limit burst=5 nodelay;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }
  

Again we find options:

  • zone: I use the limit zone previously declared in the http (core) configuration
  • burst: I allow the burst to 5 maximum connections
  • nodelay: if you want requests to still be served even if they are slowed down, use the nodelay parameter.

All that remains is to restart nginx.

Application Configurations

By default, I use www.deimos.fr to redirect to other VirtualHosts:

  server {
    include listen_port.conf;
    listen 443 ssl;

    server_name www.deimos.fr;

    access_log /var/log/nginx/www.deimos.fr_access.log;
    error_log /var/log/nginx/www.deimos.fr_error.log;

    # Blog redirect
    rewrite ^/$ $scheme://blog.deimos.fr permanent;
    rewrite ^/blog(/.*)?$ $scheme://blog.deimos.fr$1 permanent;

    # Blocnotesinfo redirect
    rewrite ^/blocnotesinfo(/.*)?$ $scheme://wiki.deimos.fr$1 permanent;

    # Piwik
    rewrite ^/piwik(/.*)?$ $scheme://piwik.deimos.fr$1 permanent;

    # Gitweb
    rewrite ^/gitweb(/.*)?$ $scheme://git.deimos.fr$1 permanent;

    # Drop config
    include drop.conf;
}
  

Server Status

By default, it’s possible to get information about the server status, like this:

  server {
    include listen_port.conf;
    listen 443 ssl;

    server_name www.deimos.fr;

    access_log /var/log/nginx/www.deimos.fr_access.log;
    error_log /var/log/nginx/www.deimos.fr_error.log;

    # Nginx status
    location /server-status {
        # Turn on nginx stats
        stub_status on;
        # I do not need logs for stats
        access_log off;
        # Allow from localhost
        allow 127.0.0.1;
        # Deny others
        deny all;
    }

    # PHP-FPM status
    location /php-fpm_status {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        # I do not need logs for stats
        access_log off;
        # Allow from localhost
        allow 127.0.0.1;
        # Deny others
        deny all;
    }

    # Cache APC
    location /apc-cache {
        root /usr/share/doc/php-apc;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
        # I do not need logs for stats
        access_log off;
        # Allow from localhost
        allow 127.0.0.1;
        # Deny others
        deny all;
    }

    # Blog redirect
    rewrite ^/$ $scheme://blog.deimos.fr permanent;
    rewrite ^/blog(/.*)?$ $scheme://blog.deimos.fr$1 permanent;

    # Blocnotesinfo redirect
    rewrite ^/blocnotesinfo(/.*)?$ $scheme://wiki.deimos.fr$1 permanent;

    # Piwik
    rewrite ^/piwik(/.*)?$ $scheme://piwik.deimos.fr$1 permanent;

    # Gitweb
    rewrite ^/gitweb(/.*)?$ $scheme://git.deimos.fr$1 permanent;

    # Drop config
    include drop.conf;
}
  

Configure the authorized addresses properly or place authentication via htpasswd.

Here’s the result when accessing the page (http://www.deimos.fr/server-status):

  Active connections: 11
server accepts handled requests
 8109 8109 58657
Reading: 0 Writing: 1 Waiting: 10
  

Wordpress

For the configuration of Wordpress under Nginx, here’s an example:

  server {
    include listen_port.conf;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;
    ssl_session_timeout 5m;

    server_name blog.deimos.fr;
    root /usr/share/nginx/www/deimos.fr/blog;
    index index.php;

    access_log /var/log/nginx/blog.deimos.fr_access.log;
    error_log /var/log/nginx/blog.deimos.fr_error.log;

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

    location ~ \.php$ {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }

    # Drop config
    include drop.conf;

    # BEGIN W3TC Browser Cache
    gzip on;
    gzip_types text/css application/x-javascript text/x-component text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
    location ~ \.(css|js|htc)$ {
        expires 31536000s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    }

    location ~ \.(html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml)$ {
        expires 3600s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    }

    location ~ \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
        expires 31536000s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    }
    # END W3TC Browser Cache

    # BEGIN W3TC Minify core
    rewrite ^/wp-content/w3tc/min/w3tc_rewrite_test$ /wp-content/w3tc/min/index.php?w3tc_rewrite_test=1 last;
    rewrite ^/wp-content/w3tc/min/(.+\.(css|js))$ /wp-content/w3tc/min/index.php?file=$1 last;
    # END W3TC Minify core
}
}
  

Mediawiki

For setting up Mediawiki with Nginx and short URLs, here’s the configuration to adopt. I’ve also added SSL and forced redirects from the login page to SSL:

  server {
    include listen_port.conf;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;
    ssl_session_timeout 5m;

    server_name wiki.deimos.fr wiki.m.deimos.fr;
    root /usr/share/nginx/www/deimos.fr/blocnotesinfo;

    client_max_body_size 5m;
    client_body_timeout 60;

    access_log /var/log/nginx/wiki.deimos.fr_access.log;
    error_log /var/log/nginx/wiki.deimos.fr_error.log;

    location / {
        rewrite ^/$ $scheme://$host/index.php permanent;
        # Short URL redirect
        try_files $uri $uri/ @rewrite;
    }

    location @rewrite {
        if (!-f $request_filename){
            rewrite ^/(.*)$ /index.php?title=$1&$args;
        }
    }

    # Force SSL Login
    set $ssl_requested 0;
    if ($arg_title ~ Sp%C3%A9cial:Connexion) {
        set $ssl_requested 1;
    }
    if ($scheme = https) {
        set $ssl_requested 0;
    }
    if ($ssl_requested = 1) {
        return 301 https://$host$request_uri;
    }

    # Drop config
    include drop.conf;

    # Deny direct access to specific folders
    location ^~ /(maintenance|images)/ {
        return 403;
    }

    location ~ \.php$ {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }

    location = /_.gif {
        expires max;
        empty_gif;
    }

    location ^~ /cache/ {
        deny all;
    }

    location /dumps {
        root /usr/share/nginx/www/deimos.fr/blocnotesinfo/local;
        autoindex on;
    }

    # BEGIN W3TC Browser Cache
    gzip on;
    gzip_types text/css application/x-javascript text/x-component text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
    location ~ \.(css|js|htc)$ {
        expires 31536000s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    }

    location ~ \.(html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml)$ {
        expires 3600s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    }

    location ~ \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
        expires 31536000s;
        add_header Pragma "public";
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header X-Powered-By "W3 Total Cache/0.9.2.4";
        try_files $uri $uri/ @rewrite;
    }
    # END W3TC Browser Cache
}
  

Gitweb

With Nginx, you need to allow certain extensions in php-fpm:

  [...]
security.limit_extensions = .php .php3 .php4 .php5 .cgi
[...]
  

And here’s a configuration example to adapt to your needs:

  server {
    listen 80;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;
    ssl_session_timeout 5m;

    server_name git.deimos.fr;
    root /usr/share/gitweb/;

    access_log /var/log/nginx/git.deimos.fr_access.log;
    error_log /var/log/nginx/git.deimos.fr_error.log;

    index gitweb.cgi;

    location /gitweb.cgi {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass  unix:/run/fcgiwrap.socket;
    }
}
  

Next we’ll need a cgi wrapper:

  aptitude install fcgiwrap
  

Create the link, then reload the configuration:

  cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/git.deimos.fr .
/etc/init.d/nginx reload
/etc/init.d/fcgiwrap restart
/etc/init.d/php5-fpm reload
  

Git

I spent quite a bit of time making Git over http(s) and Gitweb coexist, but I got it working.

Here’s the method I used:

  server {
    listen 80;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;
    ssl_session_timeout 5m;

    server_name git.deimos.fr;
    root /usr/share/gitweb/;

    access_log /var/log/nginx/git.deimos.fr_access.log;
    error_log /var/log/nginx/git.deimos.fr_error.log;

    index gitweb.cgi;

    # Drop config
    include drop.conf;

    # Git over https
    location /git/ {
        alias /var/cache/git/;
        if ($scheme = http) {
            rewrite ^ https://$host$request_uri permanent;
        }
    }

    # Gitweb
    location ~ gitweb\.cgi {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass  unix:/run/fcgiwrap.socket;
    }
}
  

Here I have my git over https working (http is redirected to https) and my gitweb also since everything that matches gitweb.cgi is matched. Now, for the git part, we’ll need to authorize the repositories we want. For this, we’ll need to rename a file in our repository and run a command:

  cd /var/cache/git/myrepo.git
hooks/post-update{.sample,}
su - www-data -c 'cd /var/cache/git/myrepo.git && /usr/lib/git-core/git-update-server-info'
  

Replace www-data with the user who has rights to the repository. Use www-data so that nginx has the rights. Then, you have permission to clone:

  git clone http://www.deimos.fr/git/deimosfr.git deimosfr
  

Piwik

For Piwik, here’s the configuration:

  server {
    include listen_port.conf;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;
    ssl_session_timeout 5m;

    server_name piwik.deimos.fr;
    root /usr/share/nginx/www/deimos.fr/piwik;
    index index.php;

    access_log /var/log/nginx/piwik.deimos.fr_access.log;
    error_log /var/log/nginx/piwik.deimos.fr_error.log;

    # Drop config
    include drop.conf;

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

    location ~ \.php$ {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }
}
  

ownCloud

ownCloud 4.X

Here’s the configuration for ownCloud 4.X with Nginx:

  server {
    include listen_port.conf;
    listen 443 default ssl;
    ssl on;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;

    server_name owncloud.deimos.fr;
    root /usr/share/nginx/www/deimos.fr/owncloud;
    index index.php;
    client_max_body_size 1024M;

    access_log /var/log/nginx/cloud.deimos.fr_access.log;
    error_log /var/log/nginx/cloud.deimos.fr_error.log;

    # Force SSL
    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    # deny direct access
    location ~ ^/(data|config|\.ht|db_structure\.xml|README) {
        deny all;
    }

    # default try order
    location / {
        try_files $uri $uri/ @webdav;
    }

    # owncloud WebDAV
    location @webdav {
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS on;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }

    location ~ \.php$ {
        try_files $uri = 404;
        fastcgi_cache mycache;
        fastcgi_cache_key $request_method$host$request_uri;
        fastcgi_cache_valid any 1h;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS on;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }

    # Drop config
    include drop.conf;
}
  

ownCloud 5.X

And for version 5:

  server {
    include listen_port.conf;
    listen 443 default ssl;
    ssl on;

    ssl_certificate /etc/nginx/ssl/deimos.fr/server-unified.crt;
    ssl_certificate_key /etc/nginx/ssl/deimos.fr/server.key;

    server_name cloud.deimos.fr;
    root /usr/share/nginx/www/deimos.fr/owncloud;
    index index.php;
    client_max_body_size 1024M;

    access_log /var/log/nginx/cloud.deimos.fr_access.log;
    error_log /var/log/nginx/cloud.deimos.fr_error.log;

    # Force SSL
    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    rewrite ^/caldav((/|$).*)$ /remote.php/caldav$1 last;
    rewrite ^/carddav((/|$).*)$ /remote.php/carddav$1 last;
    rewrite ^/webdav((/|$).*)$ /remote.php/webdav$1 last;

    error_page 403 = /core/templates/403.php;
    error_page 404 = /core/templates/404.php;

    location ~ ^/(data|config|\.ht|db_structure\.xml|README) {
            deny all;
    }

    location / {
        rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
        rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
        rewrite ^/.well-known/carddav /remote.php/carddav/ redirect;
        rewrite ^/.well-known/caldav /remote.php/caldav/ redirect;
        rewrite ^(/core/doc/[^\/]+/)$ $1/index.html;
        try_files $uri $uri/ index.php;
    }

    location ~ ^(?<script_name>.+?\.php)(?<path_info>/.*)?$ {
        try_files $script_name = 404;
        fastcgi_cache_valid any 1h;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }

    location ~* ^.+.(jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
        expires 30d;
        # Optional: Don't log access to assets
        access_log off;
    }

    # Drop config
    include drop.conf;
}
  

Firefox Sync Server

For Firefox Server Sync, it’s proxification. We forward requests to another service:

  server {
    include listen_port.conf;
    listen 443 ssl;

    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_session_timeout 5m;

    # Force SSL
    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    server_name firefoxsync.deimos.fr;

    access_log /var/log/nginx/firefoxsync.deimos.fr_access.log;
    error_log /var/log/nginx/firefoxsync.deimos.fr_error.log;

    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 10;
        proxy_pass http://localhost:5000/;
    }

    # Drop config
    include drop.conf;
}
  

Selfoss

  server {
    include listen_port.conf;

    server_name feed.deimos.fr;
    root /usr/share/nginx/www/selfoss;
    index index.php;

    access_log /var/log/nginx/feed.deimos.fr_access.log;
    error_log /var/log/nginx/feed.deimos.fr_error.log;

    location ~* \ (gif|jpg|png) {
        expires 30d;
    }

    location ~ ^/favicons/.*$ {
        try_files $uri /data/$uri;
    }

    location ~* ^/(data\/logs|data\/sqlite|config\.ini|\.ht) {
        deny all;
    }

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

    location ~ \.php$ {
        client_body_timeout 360;
        send_timeout 360;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_intercept_errors on;
    }

    # Drop config
    include drop.conf;
}
  

Seafile

  server {
    include listen_port.conf;
    server_name seafile.deimos.fr;
    # Force redirect http to https
    rewrite ^ https://$http_host$request_uri? permanent;
}

server {
    include ssl/deimos.fr_ssl.conf;

    include pagespeed.conf;
    server_name seafile.deimos.fr;

    root /usr/share/nginx/www/deimos.fr/seafile;

    access_log /var/log/nginx/seafile.deimos.fr_access.log;
    error_log /var/log/nginx/seafile.deimos.fr_error.log;

    # Max upload size
    client_max_body_size 1G;

    location / {
        fastcgi_pass    127.0.0.1:8090;
        fastcgi_param   SCRIPT_FILENAME     $document_root$fastcgi_script_name;
        fastcgi_param   PATH_INFO           $fastcgi_script_name;
        fastcgi_param   SERVER_PROTOCOL     $server_protocol;
        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   SERVER_ADDR         $server_addr;
        fastcgi_param   SERVER_PORT         $server_port;
        fastcgi_param   SERVER_NAME         $server_name;
        fastcgi_param   REMOTE_ADDR         $remote_addr;
        fastcgi_param   HTTPS               on;
        fastcgi_param   HTTP_SCHEME         https;
    }

    # Webdav
    location /seafdav {
        fastcgi_pass    127.0.0.1:8080;
        fastcgi_param   SCRIPT_FILENAME     $document_root$fastcgi_script_name;
        fastcgi_param   PATH_INFO           $fastcgi_script_name;

        fastcgi_param   SERVER_PROTOCOL     $server_protocol;
        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   SERVER_ADDR         $server_addr;
        fastcgi_param   SERVER_PORT         $server_port;
        fastcgi_param   SERVER_NAME         $server_name;

        fastcgi_param   HTTPS               on;
    }

    location /seafhttp {
        rewrite ^/seafhttp(.*)$ $1 break;
        proxy_pass http://127.0.0.1:8082;
        client_max_body_size 0;
    }

    location /media {
        root /var/www/deimos.fr/seafile/seafile-server-latest/seahub;
    }
}
  

Gogs

For Gogs, it’s simple because all you need to set is a proxy pass:

  server {
    include listen_port.conf;
    server_name git.deimos.fr;
    # Force redirect http to https
    rewrite ^ https://$http_host$request_uri? permanent;
}

server {
    include ssl/deimos.fr_ssl.conf;

    server_name git.deimos.fr;

    access_log /var/log/nginx/git.deimos.fr_access.log;
    error_log /var/log/nginx/git.deimos.fr_error.log;

    location / {
        proxy_pass    http://127.0.0.1:3000;
    }
}
  

FAQ

How to Analyze Rules

You can see the behavior of your rules (e.g., 301 redirects) using curl:

  > curl -I "http://wiki.deimos.fr"
HTTP/1.1 301 Moved Permanently
Server: nginx
Content-Type: text/html
Location: http://wiki.deimos.fr/index.php
Content-Length: 178
Accept-Ranges: bytes
Date: Wed, 16 Jan 2013 22:17:05 GMT
X-Varnish: 2038764987
Age: 0
Via: 1.1 varnish
Connection: keep-alive
  

This allows you to analyze how it works and detect potential problems.

client intended to send too large body

If you have this kind of message in your logs:

  nginx client intended to send too large body
  

It simply means that Nginx doesn’t allow you to send files as large as you want. You’ll need to use this variable in the ‘server’ part:

  client_max_body_size 10M;
  

Replace 10M with the maximum value you want to allow.

Access denied.

If you get this type of error message, it’s due to PHP-FPM having issues with extension management. You can either add the extension in question:

  security.limit_extensions = .php .php3 .php4 .php5 .cgi
  

or disable this security (not recommended):

  security.limit_extensions = false
  

Resources

Last updated 29 Aug 2014, 15:33 CEST. history