9

I have an nginx and different subdomains:

a.mydomain.com
b.mydomain.com
c.mydomain.com

Nginx has 4 rules:

1) rewrite rule:

server {
  listen 80
  server_name gl.udesk.org;

  root /nowhere;
  rewrite ^ https://a.mydomain.com$request_uri permanent;
}

2) https rule:

server {

  listen 443;
  server_name a.mydomain.com;

  root /home/a/a/public;

  ssl on;
  ssl_certificate conf.d/ssl/a.crt;
  ssl_certificate_key conf.d/ssl/a.key;
  ssl_protocols ...
  ssl_ciphers ...
  ssl_prefer_server_ciphers on;

  location ...
}

3) http default rule:

server {
  listen 80 default_server;
  return 444;
}

4) https default rule:

server {
  listen 443 default_server;
  return 444;
}

So if I start nginx and:

So why the https rules in nginx are so tricky to configure and how should I configure them properly to get the same behavior as with http version?

Update:

Creating a new certificate and adding:

ssl on;
ssl_certificate conf.d/ssl/default.crt;
ssl_certificate_key conf.d/ssl/default.key;

works now, but I would have a solution without any SSL certificate needed. Just reset all connections for all https (port 443) subdomains except https://a.mydomain.com without providing a certificate.

3
  • 3
    You can't. SSL requires a certificate before the web server knows what domain you want. It has to have a certificate to send, or it can't establish the connection to talk to the client. Commented Jun 27, 2013 at 3:17
  • 2
    @DarthAndroid: The magic is called SNI - en.wikipedia.org/wiki/Server_Name_Indication.
    – Shi
    Commented Jul 23, 2013 at 23:51
  • @Shi I'm aware of SNI - That allows the webserver to pick which certificate to send, but it still must pick a certificate. nginx isn't smart enough to realize that it doesn't need a certificate for what the user wants to do. Commented Jul 24, 2013 at 14:35

3 Answers 3

3

Don't mix Port 443 with ssl! Nginx is completely port agnostic. You can offer https through Port 80 too. Modern nginx versions allow

listen 1234 ssl;

and you don't need the ssl on; line then.

But if you want to serve https you need to specify a certificate. Your server enters https when it rewrites the http request into a https request.

You get the PROTOCOL ERROR, as the SSL Handshake is done before anything else. So return 444 isn't reached. And any SSL Handshake will need a ceritificate and a private key, to feed the encryption algorithms with the certificate/private key pair.

3

The return directive is part of the rewrite module. If you check the documentation, you may see that it works with requests. In HTTPS requests can only be made after the handshake has been finished.

There's a feature request: https://trac.nginx.org/nginx/ticket/195 and a workaround solution is provided.

server {
    listen 443 ssl;
    server_name bbb.example.com;
    ssl_ciphers aNULL;
    ssl_certificate /path/to/dummy.crt;
    ssl_certificate_key /path/to/dummy.key;
    return 444;
}
1
  • Note this will break non-SNI-capable HTTPS clients (like nginx's own proxy_pass, unless you set proxy_ssl_server_name on;) from reaching any other server_names (so essentially break the legitimate server_names that you do want to let through). See trac.nginx.org/nginx/ticket/195#comment:11 for details.
    – nh2
    Commented Jun 27, 2018 at 22:48
2

I found answer in Nginx docs:

The SSL connection is established before the browser sends an HTTP request and nginx does not know the name of the requested server. Therefore, it may only offer the default server’s certificate.

Thus, you must configure the default certificate for SSL connections to default_server that will use for request first. After that nginx will search for the best match server_name virtual server and use its config.

Also you may need to set the SSL connection settings for the default_server, even with SNI support, because some SSL settings in matched server config will be ignored. I don't understand how it works yet, but nginx receives to client ssl_certificate from matched server config and ssl_protocols from default_server config.

And now, since Nginx 1.19.4, you can use ssl_reject_handshake directive:

server {
  listen               443 ssl;
  ssl_reject_handshake on;
}

You must log in to answer this question.

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