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.


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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.