I have several servers running on the same machine, some with http only, some with both http and https. There are several server blocks defined in separate files which are included from the main config file.
I have set up a "default" server for http which will serve a generic "maintenance page" to requests that don't match any of the other server_names in the other config files. The http default server works as expected, it uses the server_name "_" and it appears first in the list of includes (because I have observed that in the case of duplicate server_names across servers, the one appearing first is used). This works great.
I would expect the same exact server block (only switching "listen 80 default_server" to "listen 443 default_server" and also instead of serving page "return 444") however it does not. Instead, it appears that the new default https server is actually grabbing all incoming https connections and causing them to fail, although the other server blocks have more appropriate server_names for the incoming requests. Removing the new default https server will cause semi-correct behavior to resume: the websites with https will all load correctly; but the websites without https will all be routed to the first https server in the include files (which according to the docs, if no "default_server" appears, then the first server block to appear will be "default").
So my question is, what is the correct way to define a "default server" in nginx for ssl connections? Why is it that when I explicitly set a "default_server" it gets greedy and grabs all connections whereas when I implicitly let nginx decide the "default server" it works like I would expect (with the incorrect server set as default and the other real servers behaving correctly)?
Here are my "default servers". Http works without breaking other servers. Https breaks other servers and consumes all.
server {
listen 443 ssl default_server;
server_name _;
access_log /var/log/nginx/maintenance.access.log;
error_log /var/log/nginx/maintenance.error.log error;
return 444;
}
server {
listen *:80 default_server;
server_name _;
charset utf-8;
access_log /var/log/nginx/maintenance.access.log;
error_log /var/log/nginx/maintenance.error.log error;
root /home/path/to/templates;
location / {
return 503;
}
error_page 503 @maintenance;
location @maintenance {
rewrite ^(.*)$ /maintenance.html break;
}
}
Any of you see what might be wrong here?
I managed to configure a shared dedicated hosting on a single IP with nginx. Default HTTP and HTTPS serving a 404 for unknown domains incoming.
1 - Create a default zone
As nginx is loading vhosts in ascii order, you should create a
00-default
file/symbolic link into your/etc/nginx/sites-enabled
.2 - Fill the default zone
Fill your
00-default
with default vhosts. Here is the zone i am using:3 - Create self signed certif, test, and reload
You will need to create a self signed certificate into
/etc/nginx/ssl/nginx.crt
.Create a default self signed certificate:
Just a reminder:
nginx -t
sudo service nginx reload
Hope it helps.
You do not have any ssl_certificate or ssl_certificate_key defined in your "default" https block. Although you do not have or want a real key for this default scenario, you still need to configure one or else nginx will have the undesired behaviour that you describe.
Create a self-signed certificate with a Common Name of * and plug it into your config and it will start to work as you want.
The "default" behaviour under this set up would be that a browser would get a warning that the certificate can not be trusted, if the user adds the certificate as an exception, the connection will be dropped by nginx and they will see their browser's default "could not connect" error message.
We basically want to avoid at all cost that the first server definition in our config file is served as a catch-all-server for SSL connections. We all know that it does that (opposed to http and using default_server config which works nicely).
This cannot be achieved declaratively for SSL (yet) so we have to code it with an IF...
The variable
$host
is the host name from the request line or the http header. The variable$server_name
is the name of the server block we are in right now.So if these two are not equal then you served this SSL server block for another host so that should be blocked.
The code does not contain specific references to your server IP addresses so it can easily be reused for other server configs without modification.
Example:
To elaborate more on the Radmilla Mustafa's answer:
Nginx uses 'Host' header for server_name matching. It does not use TLS SNI. This means that for an SSL server, nginx must be able to accept SSL connection, which boils down to having certificate/key. The cert/key can be any, e.g. self-signed.
See documentation
Hence, the solution is:
If you just want to make sure no data is returned, you can use the following snippet without certificates:
To anyone who lost as much hair over this as me (spent nearly whole day on this today). I have tried almost everything, and the thing that made it finally working correctly was this stupid line:
Building upon Ifnot's answer, my working example is:
I have no idea why this was necessary, the only principle I derived out of this was that nginx behaves very strangely when we don't give him what he wants.
If you want to be absolutely sure, then use separate IP addresses for hosts which should not answer on HTTPS and hosts which should. This also resolves the "invalid certificate" browser warning problem.
TLS SNI
SNI allows browser to pass requested server name during the SSL handshake
Nginx support TLS SNI
To check if Nginx enabled TLS SNI
and check the
error_log
that without this warningOfficial HTTPS document has more detail.
If enabled TLS SNI, the following config works fine.
If Nginx disable TLS SNI
Nginx will use default
server
certificate for all requestOfficial HTTPS document