NGINX reverse proxy "if not" statements syntax?

I have an nginx reverse proxy that I want to basically redirect any url request that isn't my specific top level domain to 404.

So far I think I want something like this at the bottom of my /etc/nginx/sites-enabled/site.conf:

server {
        listen 80;
        if ($host != *domain.com) {
                return 404 http://$host$request_uri;
        }
}

but I don't know the exact syntax here. I already have other redirect blocks above this one, I just want any url requests for IP-only hostnames or other domains to immediately get a 404 and not get passed to backend servers.

Additionally, I also have TLS passthrough I'd like to do the same thing on, but I am completely lost on how to implement an if statement on the stream block. My stream block looks like this in /etc/nginx/nginx.conf:

stream {
        map $ssl_preread_server_name $name {
                server.domain.com           server;
                www.domain.com              www;
        }
        upstream server {
                server backendip:443;
        }
        upstream www {
                server backendip2:443;
        }
        server {
                listen 443;
                proxy_pass $name;
                ssl_preread on;
        }
}

For that, would I implement something like this right below the first server block?

        server {
                listen 443;
                if ($ssl_preread_server_name != *domain.com) {
                        return 404 https://$host$request_uri;
                }
        }

I think for security I should also be including default_server in the listen line for both of these blocks with if statements, or does it matter?

I'm just assuming a lot of syntax here, so any help would be much appreciated.


I didn't know how to gracefully close a connection with a stream module, so I'm not sure if it would be a correct solution, but it worth to try (you can use any free port for the dummy server/upstream):

stream {
    map $ssl_preread_server_name $name {
        server.domain.com           server;
        www.domain.com              www;
        default                     dummy;
    }
    upstream server {
        server backendip:443;
    }
    upstream www {
        server backendip2:443;
    }
    upstream dummy {
        backend 127.0.0.1:4343;
    }
    server {
        listen 443;
        proxy_pass $name;
        ssl_preread on;
    }
    server {
        listen 4343;
        return "";
    }
}

Update

After giving the above solution a test, I can confirm it is workable. However it produces an error log entry like

WSARecv() failed (10053: An established connection was aborted by the software in your host machine) while proxying and reading from upstream, client: 127.0.0.1, server: 127.0.0.1:443, upstream: "127.0.0.1:4343", bytes from/to client:517/0, bytes from/to upstream:0/517

So after some thinking I tried this one:

http {
    server {
        listen              4343 ssl;
        ssl_certificate     /path/to/selfsigned.crt;
        ssl_certificate_key /path/to/selfsigned.key;
        return              444;
    }
}
stream {
    map $ssl_preread_server_name $name {
        server.domain.com           server;
        www.domain.com              www;
        default                     dummy;
    }
    upstream server {
        server backendip:443;
    }
    upstream www {
        server backendip2:443;
    }
    upstream dummy {
        backend 127.0.0.1:4343;
    }
    server {
        listen 443;
        proxy_pass $name;
        ssl_preread on;
    }
}

Self-signed certificate and key can be created using the following command:

openssl req -nodes -new -x509 -subj "/CN=localhost" -keyout /path/to/selfsigned.key -out /path/to/selfsigned.crt

This one closes the connection correctly.

Moreover, even this worked:

http {
    server {
        listen              80 default_server;
        return              444;
    }
}
stream {
    map $ssl_preread_server_name $name {
        server.domain.com           server;
        www.domain.com              www;
        default                     dummy;
    }
    upstream server {
        server backendip:443;
    }
    upstream www {
        server backendip2:443;
    }
    upstream dummy {
        backend 127.0.0.1:80;
    }
    server {
        listen 443;
        proxy_pass $name;
        ssl_preread on;
    }
}

Of course, curl complains on handshake attempt for the incorrect SSL version number and there are funny entries in the access log like

127.0.0.1 - - [05/Nov/2021:01:04:34 +0200] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03L\xF9[\xB2\x7F\x99\xB1(\xAC\xB4\x91}\xE2N\xC6H\xE3\xB3_\xD1\xEA\xA3C\x1D\xE5\xE5\xB6\x02\xDB\x04h\xB6 *\xCB\x0E\xC0\xF5\xB7\xAF[y|\x1B\x14_\xC2g\xEA\xA2\x1E\xB4\xC4Bj3t\xE8d\xE72vm\xF2\x1B\x00>\x13\x02\x13\x03\x13\x01\xC0,\xC00\x00\x9F\xCC\xA9\xCC\xA8\xCC\xAA\xC0+\xC0/\x00\x9E\xC0$\xC0(\x00k\xC0#\xC0'\x00g\xC0" 400 163 "-" "-"

but error log is clear.