Nginx – Why does nginx require default ssl server to have a certificate

nginxsslssl-certificate

I have a testing site with 2 domains and I want to enable SSL for both.

Also, I want to have a default server to display an error page if a client is accessing some page out of those 2 domains.

This is my nginx.conf:

server {
    listen 443 ssl;
    server_name a.com;
    ssl_certificate a.cert;
    ssl_certificate_key a.key;
}
server {
    listen 443 ssl;
    server_name b.com;
    ssl_certificate b.cert;
    ssl_certificate_key b.key;
}
server {
    listen 443 default_server ssl;
    ...
}

Problem: The default server must have a valid cert/key pair, even if the client is connecting to a.com or b.com.

If I don't specify a cert/key pair on the default ssl server, nginx gives me this error:

no "ssl_certificate" is defined in server listening on SSL port while SSL handshaking

I don't understand the logic here. With SNI enabled, when a client clearly says it's accessing a.com, which has a standalone server block in nginx.conf, why does nginx still require the default server to have a cert/key pair? Why does it even bother with the default server?

Best Answer

I have a testing site with 2 domains. I want to enable SSL for both and have a default SSL server handling errors (display an error page when clients specify incorrect domain name):

[...]

But I don't understand the logic here. Why does nginx bother with the default server if the client has clearly specified which domain it wants?

The certificate is mandatory for the ssl servers, if there was no certificate, what's the point? HTTPS wouldn't work.

You must define the ssl certificate and key, inside or outside the server block. If you define a "default_server", but you are not defining a domain, then it will always display a warning that the domain doesn't match the certificate (unless you are using wildcard certificates or some other specific use cases).

When you define name-based HTTPS servers, you have to keep in mind the following, from the official docs:

With this configuration a browser receives the default server’s certificate, i.e. www.example.com regardless of the requested server name. 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.

As you found out, a self-signed certificate accomplishes this and will allow you to present a webpage or redirect in a default_server in case there was no match for any other of the defined server_name blocks, although it (might, depending on configuration) present a warning.

If your question is why you need to define a default_server, you don't. If you don't specify a default_server block, it will use the first by alphabetical order, if someone reached your server with an unknown domain name (usually won't happen), or present the corresponding server_name.

Update

Extending my answer in response to your comment:

The quoted part is what i'm asking. Why does nginx serve the default server's certificate with SNI enabled (keep reading on that page Server Name Indication)

Check my last paragraph above, that's how virtual hosts (or server blocks in nginx terminology) and the default_server work. Nginx will serve the default_server block if you define one in a server block and the server_name doesn't match the request in the other blocks. You can define a server block with a default_server to force a catch-all to that specific block, otherwise, nginx will default to the first server block (either in nginx.conf or in sites-enabled/conf.d if included). Either you define it or nginx will choose the first block for that purpose, but a server block will be the default.

It won't serve it unless someone reaches your server with an undefined server_name, following your example, let's say someone pointed "c.com" to the IP of your nginx, then the default_server will catch that request and present whatever you defined.