82

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?

Dylan Valade
  • 5,565
  • 6
  • 42
  • 56
PascalTurbo
  • 2,189
  • 3
  • 24
  • 41

2 Answers2

104

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.

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • 1
    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... – PascalTurbo Jan 21 '13 at 13:13
  • See http://nginx.org/en/docs/http/configuring_https_servers.html#certificate_with_several_names – barbolo Apr 19 '13 at 12:51
  • 5
    Note: the second option - having `ssl_certificate` within an `if` does not work. – James Billingham Mar 05 '14 at 16:51
  • 3
    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. – Matt Jul 23 '14 at 17:03
  • 1
    Answer completely rewritten. – Déjà vu Nov 17 '14 at 05:55
  • What about the case that I want two server names share the same data but with different certificates, do I have to write two server blocks with much redundant code except the certificate part? – K.F Mar 19 '22 at 17:43
  • 1
    @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. – Déjà vu Mar 19 '22 at 18:09
  • There is a very helpful line in the `certbot` man page: "-d DOMAINS Comma-separated list of domains to obtain a certificate for". Also i don't see anyone here mentioned it, but somebody have to: in most cases it's a bad idea to publish the same content under more then one domain name. – n3ko Mar 15 '23 at 01:01
2

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

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.

If TLS SNI is enabled, the following config works fine.

# Create the self signed certificate

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"
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";
}
# Add -v to verify 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.

bfontaine
  • 18,169
  • 13
  • 73
  • 107
Steely Wing
  • 16,239
  • 8
  • 58
  • 54