98

I'd love to use nginx to serve a website with multiple domain names and SSL:

  • webmail.example.com
  • webmail.beispiel.de

Both use the same vhost so I only set the server_name twice. Problem is, that I need nginx to serve the correct ssl certificate for each domain name.

Is this possible with one vhost or do I need to set up two vhosts?

2 Answers 2

120

Edit November 2014: the initial answer is not correct and is incomplete ; it needed a refresh! here it is.

Basically, there are two cases

  • You own a wildcard certificate (or multi-domains certificate)

In this case, you may use several vhosts listening to the same IP address/https port, and both vhosts use the same certificate (listening on all interfaces), e.g.

    server {
      listen 443;
      server_name webmail.example.com;
      root /var/www/html/docs/sslexampledata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }
    
    
    server {
      listen 443;
      server_name webmail.beispiel.de;
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }

or in you specific case, having both domains served by the same data

    server {
      listen 443;
      server_name webmail.example.com webmail.beispiel.de; # <== 2 domains
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }



  • You have two(+) different certificates

The case above (one IP for all certificates) will still work with modern browsers via Server Name Indication. SNI has the client (browser) send the host it wants to reach in the request header, allowing the server (nginx) to deal with vhosts before having to deal with the certificate. The configuration is the same as above, except that each vhost has a specific certificate, crt and key.

(nginx support SNI from 0.9.8f, check your nginx server is SNI compliant)
(also, SF talks about SNI and browser support)

Otherwise, if you want to reach older browsers as well, you need several vhosts listening each to a different IP addresses/https ports, e.g.

    server {
      listen 1.2.3.4:443; # <== IP 1.2.3.4
      server_name webmail.example.com;
      root /var/www/html/docs/sslexampledata;
    
      ssl on;
      ssl_certificate /var/www/ssl/certifIP1example.crt;
      ssl_certificate_key /var/www/ssl/certifIP1example.key;
      ...
    }
    
    
    server {
      listen 101.102.103:443; <== different IP
      server_name webmail.beispiel.de;
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/certifIP2beispiel.crt;
      ssl_certificate_key /var/www/ssl/certifIP2beispiel.key;
      ...
    }

The reason is well explained here.

Sign up to request clarification or add additional context in comments.

8 Comments

Shure, this is a solution, but not a nice one. Changing one vhost means changing the other. And at least there will be 4 vhosts...
Note: the second option - having ssl_certificate within an if does not work.
From what I've read, the HTTP_HOST is in the request headers and the headers are encrypted by SSL. So you cannot inspect the HTTP_HOST before decrypting with the correct SSL cert. A catch 22.
Answer completely rewritten.
@K.F The server_name directive is associated to a server block. Thus since your certs differ (I guess one for each domain) you need 2 server blocks (one cert per block). However you can use the include file; directive to include the common settings (I added a /etc/nginx/sites-include dir in which I put all my includes...) then in each block do the include of the common part, the server_name, and add a specific cert directive.
|
5

Use TLS SNI

SNI allows browser to pass requested server name during the SSL handshake

server {
    listen       443 ssl default_server;
    server_name  "";

    ssl_certificate      default.crt;
    ssl_certificate_key  default.key;

    add_header "Content-Type" "text/plain";
    return 200 "default page";
}

server {
    listen       443 ssl;
    server_name  a.example.org;

    ssl_certificate      a.crt;
    ssl_certificate_key  a.key;

    add_header "Content-Type" "text/plain";
    return 200 "a.example.org page";
}

server {
    listen       443 ssl;
    server_name  b.example.org;

    ssl_certificate      b.crt;
    ssl_certificate_key  b.key;

    add_header "Content-Type" "text/plain";
    return 200 "b.example.org page";
}

To check if Nginx enabled TLS SNI

$ nginx -V
...
TLS SNI support enabled
...

and check in the error_log that this warning is not present:

nginx was built with SNI support, however, now it is linked dynamically to an OpenSSL library which has no tlsext support, therefore SNI is not available

Nginx HTTPS documentation has more detail.

Testing

Create self signed certificate for testing

openssl req -x509 -newkey rsa -nodes -keyout default.key -days 36500 -out default.crt -subj "/CN=example.org"

openssl req -x509 -newkey rsa -nodes -keyout a.key -days 36500 -out a.crt -subj "/CN=a.example.org"

openssl req -x509 -newkey rsa -nodes -keyout b.key -days 36500 -out b.crt -subj "/CN=b.example.org"
# Add -v to show the certificate

$ curl --insecure --resolve "a.example.org:443:127.0.0.1" https://a.example.org

a.example.org page

$ curl --insecure --resolve "b.example.org:443:127.0.0.1" https://b.example.org

b.example.org page

$ curl --insecure https://127.0.0.1

default page

Ref: Nginx TLS SNI

If Nginx disable TLS SNI: Nginx will use default server certificate for all request.

Nginx documentation:

This is caused by SSL protocol behaviour. The SSL connection is established before the browser sends an HTTP request and nginx does not know the name of the requested server. Therefore, it may only offer the default server’s certificate.

1 Comment

in curl -v stands for verbose and not validity for ssl certificate. to verify the certificate drop the --insecure flag.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.