Redirect to https unless from private network on nginx

I'd like to redirect all request coming from outside the network to https - but keep internal one s on http.

Right now I have two .conf files - one that begins so:

server {
        # listen [::]:443 ssl ipv6only=on; # managed by Certbot                                                                                                                                              
        listen 443 ssl; # managed by Certbot                                                                                                                                                                 
        server_name www.example.com , example.com;         
        # omitted stuff
}

and contains

server {
    listen 80;
    server_name www.example.com , example.com;         
    return 301 https://$host$request_uri;
}

and another that begins like this:

server {                                                                                                                                                                                                     
        listen 192.168.1.144:80;                                                                                                                                                                             
        listen 192.168.1.196:80;                                                                                                                                                                             
                                                                                                                                                                                                             
        server_name "";                                                                                                                                                                                      
        root /var/www/html;                                                                                                                                                                                  
        # omitted stuff
   }

But right now requests to http://example.com do not get redirected to https.

Why is that? what have I gotten wrong? my goal is to redirect all requests not coming from the home private network (192.168.1.*) to https, and leave internal ones alone.


Solution 1:

server_name directive uses spaces for separating domain names. Your configuration has commas, which confuses nginx.

You need to use:

server_name www.example.com example.com;

Solution 2:

I assume that with the following you are trying to make a different server block that listens on two interfaces on the local network:

server {
    listen 192.168.1.144:80;
    listen 192.168.1.196:80;
    # ...
}

For this to work your topology would look similar to this:

Topology with Nginx directly connected to the Internet

However, if your server is behing the same NAT than the clients on the private network 192.168.1.0/24, this server block would be used for the external connections, too. This topology would be similar to this:

Topology with NAT & port forwarding

With the latter, you could use e.g. the ngx_http_geo_module to separate the local network based on its clients:

geo $external {
    default         1;
    192.168.1.0/24  0;
}

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/html;
    
    if ($external) {
        return 301 https://$server_name$request_uri;
    }
}

However, from the security perspective I wouldn't recommend trusting your internal network like this, but using TLS equally for every client. That's because it's easy to get MitM position simply by having access to the local network.