Ubuntu Gutsy - Nginx, SSL and vhosts

So you've followed the Nginx self signed certificate article and now you want to configure Nginx to serve your site on the standard HTTPS port (443).

With Nginx, it is very easy to configure your virtual host to use a secure connection.


Defaults

Enabling SSL support for your site is very simple and involves the addition of just a few lines to the virtual host file.

Go ahead and open up your virtual host file:

sudo nano /etc/nginx/sites-available/domain1.com

My example vhost (details of which can be found in this article) looks like this:

server {

            listen   80;
            server_name  www.domain1.com;
            rewrite ^/(.*) http://domain1.com/$1 permanent;

           }


server {

            listen   80;
            server_name domain1.com;

            access_log /home/demo/public_html/domain1.com/logs/access.log;
            error_log /home/demo/public_html/domain1.com/logs/error.log;

            location / {

                        root   /home/demo/public_html/domain1.com/public/;
                        index  index.html;

                        }

            }

Very simple and straight forward.

Port 443

To enable the use of port 443 with our SSL certificate all we need to do is let Nginx know we want to listen on port 443 and to define the location of the certificate.

To do this, simply copy and paste the existing content so, to begin with, you have two sets of server modules that are exactly the same.

Now change the ports on the second set from 80 to 443:

server {

            listen   80;
            server_name  www.domain1.com;
            rewrite ^/(.*) http://domain1.com/$1 permanent;

           }


server {

            listen   80;
            server_name domain1.com;

            access_log /home/demo/public_html/domain1.com/logs/access.log;
            error_log /home/demo/public_html/domain1.com/logs/error.log;

            location / {

                        root   /home/demo/public_html/domain1.com/public/;
                        index  index.html;

                        }

            }



server {

            listen   443;
            server_name  www.domain1.com;
            rewrite ^/(.*) http://domain1.com/$1 permanent;

           }


server {

            listen   443;
            server_name domain1.com;

            access_log /home/demo/public_html/domain1.com/logs/access.log;
            error_log /home/demo/public_html/domain1.com/logs/error.log;

            location / {

                        root   /home/demo/public_html/domain1.com/public/;
                        index  index.html;

                        }

            }

Good.

Certificate Location

Now all we need to do is let Nginx know we want to use SSL and where the certificate is located.

As such, we need to add the following to each port 443 server module:

ssl    on;
ssl_certificate    /etc/ssl/certs/myssl.crt;
ssl_certificate_key     /etc/ssl/private/myssl.key;

Remember the locations of the crt and key files were defined during the certificate generation (see here).

So now the beginning of each port 443 server module looks like this:

server {

            listen   443;

            ssl    on;
            ssl_certificate    /etc/ssl/certs/myssl.crt;
            ssl_certificate_key    /etc/ssl/private/myssl.key;

            server_name domain1.com;

That's it.

Restart

To enable the changes we need to restart Nginx:

sudo /etc/init.d/nginx restart

Navigate

Now you are ready to navigate to your secure domain:

https://admin.domain.com

In this example, we have used a self signed certificate so you will receive a warning similar to this:

SSL Certificate Warning

All the information matches that which we entered when generating the certificate but, as it is not 'official' or 'trusted', a warning is shown.

You may also receive a second warning if you are using the certificate on a domain that does not match the Common Name setting.

However, it is perfectly fine for administration areas or private areas of your site and, of course, if you use a commercial certificate, you will not receive any warnings.

Summary

Creating a virtual host that uses an HTTPS connection to encrypt your connection is very easy to do with Nginx.

Remember a self signed certificate will produce warnings when accessed but will be fine for personal administration areas.

PickledOnion.

Article Comments:

Ben commented Fri Apr 18 18:11:52 UTC 2008:

Note to SSL neophytes like me: you can have only one SSL cert per IP.

nginx will happily let you make multiple server{} entries with different SSL certs and won't complain, but only one gets bound to port 443.

The error you will get is "host name mismatch" or "domain mismatch" in the web browser.

This is true because the SSL connection is established before any HTTP information is exchanged. So by the time the HTTP request is processed by nginx, it has already used an SSL cert.

It would make more sense if the nginx config put SSL setup outside the server{} block since SSL and vhosts don't have much to do with each other.

cs commented Sat Nov 29 18:29:12 UTC 2008:

thanx for the nice tutorial, my https is working at address domain.com

however http://domain.com, www.domain.com is not redirected to https, it shows "Welcome to nginx!"

and https://www.domain.com shows a 404 page too.

is there a way to redirect everything at domain.com to https?

thanx a lot, cs.

cs commented Mon Dec 01 10:11:05 UTC 2008:

okay, i've figured out how to redirect all access to https.

if you've followed this article your host file should look like:

' server {

        listen   443;
        server_name  www.domain1.com;
        rewrite ^/(.*) http://domain1.com/$1 permanent;

       }

server {

        listen   443;
        server_name domain1.com;

        access_log /home/demo/public_html/domain1.com/logs/access.log;
        error_log /home/demo/public_html/domain1.com/logs/error.log;

        location / {

                    root   /home/demo/public_html/domain1.com/public/;
                    index  index.html;

                    }

        }

'

and you'll have to do the following changes:

' server {

        listen   80;
        server_name  www.domain.com;
        rewrite ^/(.*) https://domain.com/$1 permanent;

       }

'

Mark commented Wed Feb 11 17:28:28 UTC 2009:

If you're using the rails ssl_requirement plugin, you'll need nginx to send the right headers, or else you'll end up with an infinite feedback loop. You'll want to add:

proxy_set_header  X-FORWARDED_PROTO https;

So you'll have:

server {
        listen  443;

        ssl    on;
        ssl_certificate    /etc/ssl/certs/myssl.crt;
        ssl_certificate_key    /etc/ssl/private/myssl.key;

location / {
        proxy_set_header  X-FORWARDED_PROTO https;
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_set_header  X-Forwarded-For proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect false;

Emre Köse commented Thu Apr 02 09:04:11 UTC 2009:

Thank you for this tutorial but I need to change my nginx settings to overcome SSL vulnerabilities. How can I do that?

Mark Chodos commented Sat May 02 16:25:40 UTC 2009:

Adding these 2 lines to the server block in our config satisfied the requirements of our security scanner for PCI compliance:

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

Emre Köse commented Fri Jan 08 13:36:21 UTC 2010:

Hi Mark, Thank you!

Chris Hepner commented Sun Apr 18 19:27:28 UTC 2010:

One important item of note - if you have multiple name-based vhosts on the same IP, the site with the SSL certificate must be the default for port 443, or it won't establish a connection at all.

The easiest way to do this is to change your site's vhost as follows:

server { listen 443 default;

server_name myssldomain.com; ssl on; ... }

This should allow the connection to establish correctly. Thanks for the tutorials!

Skip Levens commented Tue Jan 15 19:07:49 UTC 2013:

+1 on Chris' comment in case anyone is having this issue. In my case I have multiple, virtual hosts under nginx, and suddenly they were all throwing error 400's.. Simply adding 'default' as Chris suggested fixed it right up.

Want to comment?


(not made public)

(optional)

(use plain text or Markdown syntax)