Here's a situation I am having a hard time understanding. I have nginx setup with two blocks for hosts like the following

server {
    server_name _;
    listen *:443 default_server deferred;
    return 444;
server {
    listen      443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.ca;


The catch-all has a self-generated ssl certificate and the block to my application has a working SSL certificate. This setup is working for web browsers. If a web browser goes to https://www.example.com then they go to my application but if they go to htpps:// then it will just return nothing. But now I've run into a very weird situation. I was getting some requests to the domain that were failing (coming from a script) so I tried to debug. First I tried curling the url:

$ curl https://www.example.ca
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none

I thought this was weird because the browsers all show the the certs as working as well as www.ssllabs.com shows that it's working. The next step I tried was viewing the SSL cert on the command line:

echo | openssl s_client -showcerts -connect www.example.ca:443

This command actually shows my the SSL certificate for the catch all server block and not the example.ca block. If I add the ip address to the second nginx block then I see the ssl cert for example.ca. So now I know that openssl and curl are getting the wrong ssl cert because they are being served the wrong cert but I don't understand why?

I don't know much about openssl but all I can think of is either openssl/curl are wrongly converting the hostname to an ip address or they are forgetting to add a correct header? This is super strange and if anyone knows more I would appreciate some feedback.

1 Answer 1


Vhost servers like nginx with multiple certs (and keys) use the Server Name Indication (SNI) extension to select the correct (vhost and) cert, but by default openssl s_client does not send SNI so you get the default cert; add -servername $hostname as described on the man page (right after -connect). There are lots of duplicates of this part on several other Stacks but I can't find one here. (UPDATE: OpenSSL 1.1.1 in 2018 fixed this; SNI is now the default unless you specify -noservername.)

OTOH curl does send SNI automatically (as do browsers and ssllabs) unless you have a really old version of either curl itself or the middleware it uses (which is not always OpenSSL; check curl -V uppercase). Try adding -v to get more details about what it's doing and getting, and if that's not enough get a wire trace with tcpdump/Wireshark/etc.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .