Check config

nginx -t

Gzip compression

gzip  on;
gzip_comp_level 3;
gzip_proxied any;
# what mimetypes to gzip? otherwise only html gets zipped
gzip_types      text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/png image/gif image/jpeg image/x-icon image/bmp;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 1400; # in bytes
gzip_vary  on;        # allow the client to cache it
  • If nginx is requested a foo.jpg and finds a foo.jpg.gz it will send that file instead of zipping foo.jpg


# let several request be made in one connection
# hold connection open for max x seconds
keepalive_timeout 60;


auth_basic            "You shall not pass!";
auth_basic_user_file   /etc/nginx/htpasswd;
  • Use htpasswd to create the file
  • If you would like to require auth except for one ip
# allow puppet master to post reports

# require auth from all else
deny all;
satisfy  any;
auth_basic            "You must login";
auth_basic_user_file   /etc/nginx/htpasswd;""

SSL config

listen 443 ssl;
ssl on;
ssl_certificate /etc/ssl/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_dhparam /etc/ssl/dhparam.pem;

ssl_protocols TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains";

Redirect http to https

server {
      listen 80;
      server_name whatever;
      return 301 https://$host$request_uri;


location /old_stuff/ {
   rewrite   ^/old_stuff/(.*)$  /new_stuff/$1  permanent;

Security tricks

  • Dont serve version control files, sql / json dumps
location ~ (\.git)|(CVS)|(\.svn)|(\.hg)|(\.ht)|(sql)|(dump)|(json) {
   access_log /var/log/nginx/security.log;
   return 404;
  • Dont serve password files
location ~ (\.ht)|(pass) {
   access_log /var/log/nginx/security.log;
   return 404;
  • Dont serve backup files
location ~ (\.old$)|(~$)|(^#)|(\.bak$)|(\.orig$)|(Kopievon)|(tmp) {
   access_log /var/log/nginx/security.log;
   return 404;
  • Dont serve logs and docs
location ~ /(doc)|(log)|(documentation) {
   access_log /var/log/nginx/security.log;
   return 404;
  • Dont serve dot files and dirs
location ~ /\. {
   access_log /var/log/nginx/security.log;
   return 404;
  • Hide server version number
http {
   server_tokens off;


 upstream myservers {

server {
   location / {
     proxy_pass http://myservers;

Traffic shaping

limit_rate_after 1g;
limit_rate       50k;

Request size

client_max_body_size 2M;


# [ debug | info | notice | warn | error | crit ]
error_log  /var/log/nginx.error_log  debug

uWSGI Virtualhost for serving Django

server {
    listen  80;
    root /srv/http/balle/balle;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /static {
        alias /srv/http/balle/static;
        gzip on;
        expires 30m;

    location /media {
        gzip on;
        expires 24h;  # otherwise i client wont cache

    location / {
        uwsgi_pass unix:///var/run/uwsgi/balle.sock;
        include uwsgi.params;


uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

Gunicorn as WSGI server

  • pip install gunicorn
  • gunicorn -w 4 -D –bind unix:/tmp/gunicorn.sock myproject:app
server {
  location / {
    #proxy_pass http://localhost:8000;
    proxy_pass unix:/tmp/gunicorn.sock;


  • Install php-fpm and start server
  • Add the following to your server directive
location ~ \.php$ {
   include fastcgi.conf;
   fastcgi_intercept_errors on;
   fastcgi_pass    unix:///var/run/php-fpm/php-fpm.sock;

Puppet Passenger

user  nginx;
worker_processes  1;

pid        /var/run/;

events {
  worker_connections  1024;

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

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
  '$status $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';

  sendfile        on;
  tcp_nopush      on;

  keepalive_timeout  65;

  # Passenger needed for puppet
  passenger_root  /usr/share/rubygems/gems/1.8/passenger-3.0.21;
  passenger_ruby  /usr/bin/ruby;
  passenger_max_pool_size 15;

  ssl                  on;

  ssl_protocols  SSLv2 SSLv3 TLSv1;
  ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
  ssl_prefer_server_ciphers   on;

  server {
    listen                     8140 ssl;
    server_name      ;

    passenger_enabled          on;
    passenger_set_cgi_param    HTTP_X_CLIENT_DN $ssl_client_s_dn;
    passenger_set_cgi_param    HTTP_X_CLIENT_VERIFY $ssl_client_verify;

    access_log  /var/log/puppet/passenger_access.log  main;
    error_log  /var/log/puppet/passenger_error.log warn;

    root                       /usr/share/puppet/rack/puppetmasterd/public/;

    ssl_certificate      /var/lib/puppet/ssl/certs/;
    ssl_certificate_key  /var/lib/puppet/ssl/private_keys/;
    ssl_crl                    /var/lib/puppet/ssl/ca/ca_crl.pem;
    ssl_client_certificate     /var/lib/puppet/ssl/certs/ca.pem;
    ssl_ciphers                SSLv2:-LOW:-EXPORT:RC4+RSA;
    ssl_prefer_server_ciphers  on;
    ssl_verify_client          optional;
    ssl_verify_depth           1;
    ssl_session_cache          shared:SSL:128m;
    ssl_session_timeout        5m;