Proxying Zotonic with nginx

It is possible to put Zotonic behind the nginx <http://nginx.org/> web server, for example if you have other, non-Zotonic virtual hosts running on your system.

When proxying, don’t forget to check the config files of the sites you are planning to server (the user/sites/your_site/config files). The hostname value should not contain any port number, if you run from port 80: {hostname, "test.zotonic.com"}.

Below is a configuration file we use to proxy nginx to zotonic. Be sure to replace all occurrences of test.zotonic.com with your own hostname:

server {
      listen 80;
      server_name  test.zotonic.com;

      access_log  /var/log/nginx/test.zotonic.com.access.log;
      error_log  /var/log/nginx/test.zotonic.com.error.log;

      keepalive_timeout 65;
      gzip off;

      location / {
          proxy_pass http://127.0.0.1:8000/;
          proxy_redirect off;

          proxy_set_header   Host             $host;
          proxy_set_header   X-Real-IP        $remote_addr;
          proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

          client_max_body_size       50m;
          client_body_buffer_size    128k;

          proxy_connect_timeout      90;
          proxy_send_timeout         90;
          proxy_read_timeout         90;

          proxy_buffer_size          4k;
          proxy_buffers              4 32k;
          proxy_busy_buffers_size    64k;
          proxy_temp_file_write_size 64k;
      }

      location /close-connection {
           keepalive_timeout 0;
           empty_gif;
      }
}

See the nginx documentation for more information on its configuration procedure.

Use nginx for SSL termination

It is possible to use nginx to terminate SSL. If this is done, then the protocol configuration option needs to be set to https. Otherwise Zotonic doesn’t know that the site is running on HTTPS.

This is an example configuration for nginx:

server {
    listen 443 ssl;
    listen [::]:443 ssl ipv6only=on;
    server_name hostname.tld;

    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # not possible to do exclusive
    ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';
    add_header Strict-Transport-Security max-age=15768000; # six months

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;

    ssl_dhparam /etc/nginx/dhparam.pem;
    ssl_certificate /path/to/ssl.crt;
    ssl_certificate_key /path/to/ssl.key;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:8000/;
    }
}