Nginx vhost inherits ssl_certificate* directives from default block?
<p>Here's what I'm ultimately trying to do, for full disclosure: I am trying to set up TLS on multiple vhosts using a single certificate with SNI: one vhost for the apex, and another for a separate host. That is, I want to serve <code>https://example.com</code> and <code>https://host.example.com</code> using the same certificate. Both hosts resolve to the same IPv4 address.</p>

<p>I'm having trouble understanding what Nginx is doing with <code>ssl_certificate*</code> directives. Those directives are only defined in the default server block, but are being "picked up" in another block anyway.</p>

<p>I started with a default server block to 404.</p>

<pre><code>server {
  return 404;
}
</code></pre>

<p>As expected,</p>

<pre><code>$ curl --head http://example.com
HTTP/1.1 404 Not Found
[...]
</code></pre>

<p>Then I created the first example.com block to redirect http to https on the apex:</p>

<pre><code>server {
  listen 80;
  server_name example.com;
  return 301 https://example.com;
}
</code></pre>

<p>And this works too:</p>

<pre><code>$ curl --head http://example.com
HTTP/1.1 301 Moved Permanently
[...]
Location: https://example.com

$ curl --head http://host.example.com
HTTP/1.1 404 Not Found
[...]
</code></pre>

<p>Then I configured TLS on the apex:</p>

<pre><code>server {
  listen 443 ssl;
  ssl_certificate [...];
  ssl_certificate_key [...];
  server_name example.com;
}
</code></pre>

<p>So far, so good: http redirects to https, and https works.</p>

<pre><code>$ curl --head http://example.com
HTTP/1.1 301 Moved Permanently
[...]
Location: https://example.com

$ openssl s_client -connect example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
</code></pre>

<p>Then I ran into a brief snag. I hadn't yet said a word about <code>host.example.com</code>, and yet when I tried to connect over TLS:</p>

<pre><code>$ curl --head http://host.example.com
HTP/1.1 404 Not Found
[...]

$ openssl s_client -connect host.example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: host.example.com

HTTP/1.1 200 OK
[... content from example.com...]
</code></pre>

<p>The default block is responding to <code>http://host.example.com</code>, as it should. But <code>https://host.example.com</code> is matching the server block for <code>https://example.com</code>. </p>

<p>So after a bit of head scratching, I realized I needed <em>two</em> default blocks: one for each port.</p>

<pre><code>server {
  listen 443 ssl;
  ssl_certificate [...];
  ssl_certificate_key [...];
  return 404;
}
</code></pre>

<p>Confirming this is what I needed:</p>

<pre><code>$ openssl s_client -connect host.example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: host.example.com

HTTP/1.1 404 Not Found
[...]
</code></pre>

<p>But then I realized something. And here is where my confusion starts. I shouldn't be able to, and don't understand why I was able to, connect to example.com:443 (or host.example.com:443) over TLS, <em>at all</em>.</p>

<p>When I created the second default server for port 443, I <em>removed</em> the <code>ssl_certificate</code> and <code>ssl_certificate_key</code> directives from the server block for <a href="https://example.com" rel="nofollow">https://example.com</a>. They are only defined in the default server block.</p>

<pre><code>server {
  listen 443 ssl;
  ssl_certificate [...];
  ssl_certificate_key [...];
  return 404;
}

server {
  listen 443 ssl;
  server_name example.com;
  # No ssl_certificate* directives!
}
</code></pre>

<p>And yet, somehow, the certificate I defined in the <em>default</em> server block ends up being used when I try to connect to <a href="https://example.com" rel="nofollow">https://example.com</a>:</p>

<pre><code>$ openssl s_client -connect example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
[...]
</code></pre>

<p>And I also confirmed that visiting the url <code>https://example.com</code> in a browser serves up an actual page.</p>

<p>So, the configuration for <a href="https://example.com" rel="nofollow">https://example.com</a> seems to be <em>inheriting</em> the settings for TLS from the default block, but then <em>applying</em> the rest of the settings from the server block that matches. Is that what's happening? I've tried searching on most of the matrix of keywords I can think of: "nginx", "tls", "ssl", "vhost", "default server block", "inherits", "inheritance"; but I can't find anything, and the documentation seems to be silent. It's probably written somewhere, but I'm not looking in the right place.</p>

<p>ADDED:</p>

<p>Full configuration, which is a basic Debian install, mostly unchanged from the package defaults.</p>

<pre><code>user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# above line loads:
# ngx_http_auth_pam_module, ngx_http_geoip_module, ngx_http_image_filter_module, ngx_http_xslt_filter_module, ngx_mail_module, ngx_stream_module

events {
  worker_connections 768;
}

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  server_tokens off;
  include /etc/nginx/mime.types; # nothing unusual in here
  default_type application/octet-stream;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # drop SSLv3
  ssl_prefer_server_ciphers on;
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log
  gzip on;
  gzip_disable "msie6";
  include /etc/nginx/conf.d/*.conf; # empty directory, includes nothing
  include /etc/nginx/sites-enabled/*; # includes single file which defines server blocks as above
}
</code></pre>

<p>And that's basically it.</p>

<p>EDIT AGAIN:</p>

<p>Here is the full server config file, all in one place.</p>

<pre><code>server {
  listen 80;
  return 404;
}

server {
  listen 443 ssl;
  ssl_certificate [...];
  ssl_certificate_key [...];
  return 404;
}

server {
  listen 80;
  server_name example.com;
  return 301 https://example.com;
}

server {
  listen 443 ssl;
  server_name example.com;
  root /srv/www/example.com;
  index index.html;
}
</code></pre>

<p>With this configuration, visiting <a href="https://example.com" rel="nofollow">https://example.com</a> works, and uses the certificate configured in the default server block. Why?</p>