1

I have a server that runs multiple virtual hosts. Some of those sites have SSL components and others don't. Today I noticed that if I try to access one of the non-SSL sites via https, Nginx just finds a virtual host that does use SSL and attempts to render it -- giving the familiar "untrusted" warning before doing so.

I've tried several solutions, but none quite work and I've exhausted my Nginx knowledge and/or reading comprehension. Ideally, I'd like to see a 404 error if a site is accessed via a protocol that it doesn't support.

I tried various edits to the default server block, but none worked. I also tried capturing and redirecting port 443 access to that particular site, but that (predictably) failed as well.

Can someone point me in the right direction?

3
  • 1
    No excuse to have sites that don't run SSL these days anyway. Just get SSL working on the rest.
    – Will
    Jun 15, 2015 at 21:19
  • @Will not every single website needs to be encrypted or authenticated; there are more than a couple reasons to choose to not use ssl.
    – pete
    Jul 14, 2015 at 3:06
  • Run two IPs, block put all not secure sites on an IP with Port 443 blocked, a connection refused looks better than an invalid certificate.... Oct 2, 2017 at 3:20

5 Answers 5

3

No can do. Without Server Name Indication (SNI), the hostname is part of the encrypted payload. Even with SNI, a browser won't accept a redirect from a HTTPS URL without first going through the HTTPS handshake + validation process.

If you want, you could use two IP addresses, one for your secure site and one for the non-secure sites, and only listen to 443 on the secure IP.

3
  • it might be possible if i understand this old answers correctly: serverfault.com/questions/373929/… May 6, 2014 at 15:17
  • 1
    @DennisNolte There'll still be the "untrusted" warning in those. Changing the default may also affect the site you intend to have HTTPS on.
    – ceejayoz
    May 6, 2014 at 15:21
  • Well, damn. I was hoping there was a way to detect that the incoming request had an unrecognized "signature" and just drop it to a 404 the same way it does a domain that resolves to the server, but doesn't have a virtual host configured. Thanks. May 6, 2014 at 19:08
2

I don't think any of the other answers covers the whole story:

What you want to do is possible for a server to do, because of SNI. However, nginx, even though it supports SNI, does not support doing what you want to do. nginx will react to an unmatched SNI name by treating the request as if it's for your default SSL site (and if you have marked no SSL site as your default, the first SSL site in its configuration). This will usually result in the user seeing a certificate warning notice, because the name on the certificate nginx sends doesn't match the name the client requested.

The RFC for SNI specifies that the server MAY consider an unrecognized SNI name as fatal:

If the server understood the client hello extension but does not recognize the server name, it SHOULD send an "unrecognized_name" alert (which MAY be fatal).

As far as I can tell, however, both Apache and nginx proceed with the connection and treat it in the same way as they would treat an unmatched or missing "Host:" header, which is to select the virtual host marked as default for that address:port.

Unless you have acquired an SSL certificate matching every possible domain name the client may use when contacting your server, it is not possible to prevent the user seeing a warning about an invalid certificate before proceeding. Even if you do a redirect as soon as they come to the site, this will be processed only after the invalid certificate is accepted.

2017 update: since it's easy to get free SSL certificates for all your domains now, you could get SSL certs for all your domains, even if you don't intend to serve actual content though HTTPS on them, just to ensure that any redirects or error messages will work without any warning about mismatched certificates.

1

You can do what you are looking for by adding a server entry with a fake ( I use self-signed for localhost ) ssl certificate that returns the error you want. This should be the first server block or at least before any other ssl blocks. Note that I normally use deny all but in this case added the 404.

server {
        listen       443 ssl default_server;
        server_name    localhost;
        ssl_certificate_key  /etc/ssl/private/localhost.key;
        ssl_certificate /etc/ssl/certs/localhost.crt;
#       deny  all;
        return 404;
}

To generate the self signed certificate you can use the following commands

openssl genrsa 4096 > localhost.key
openssl req -new -sha256 -key localhost.key -subj "/CN=localhost" > localhost.csr
openssl x509 -req -days 3650 -in localhost.csr -signkey localhost.key -out localhost.crt
chmod 600 localhost.key
mv localhost.key /etc/ssl/private/
mv localhost.crt /etc/ssl/certs/
0

It is not possible with NGinx (or, by default, with anything else).

If you totally do not care about older clients (such as the ones using Chrome or IE on Windows XP) and you are ok supporting only clients that use the SNI extension of the TLS protocol, then you can use haproxy in front of nginx to do this.

Another solution would be to put all non-SSL websites on a completely separate IP address that does not have a listen on the https port.

-1

I'm not sure what exactly you tried, but if you do have SNI support, one method is to form a catch-all:

server {
    listen [::]:443 default_server ssl;
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

    server {
    listen  [::]:443 ssl;
        listen  443 ssl;
    server_name  ssl.site.net;
    ssl_certificate      your.crt;
    ssl_certificate_key  your.key;
....etc...

which will dump non-matched servers (from a config i used to use, i don't currently, so apply grain-of-salt) As others pointed out, i don't believe you can do a redirect this way, only a drop.

For non-SNI support (read: legacy) you could always get a cheap certificate for wildcard-catching, then you'd be able to handle all connections and redirect as wish with no issues.

Edit: on second thought, if i recall, because nginx supports SNI, even if a client that doesnt support SNI connects, it will still cause nginx to return 444 while trying to match a certificate to send...thoughts?

2
  • Yes, if a client without SNI connects it would go to your default_server for that listening port, which will result in the return 444. There's not really such a thing as a wildcard certificate from an actual CA, and multiple domain certificates are never cheap. Furthermore, in my testing the return 444 was only effective after the SSL connection was established, meaning nginx still served up a certificate for one of my other SSL sites, which had to be accepted, before the return 444 had an effect. However, my testing may have been flawed - I did not test with no other SSL sites. Jul 14, 2015 at 1:02
  • @thomasrutter - you are correct; i believe i was thinking of wildcard subdomains, as opposed to multiple domain catching at the time. i'm not running nginx anymore so i can't test readily
    – pete
    Jul 14, 2015 at 2:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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