Search code examples
nginxsubdomain

Nginx redirecting to the wrong site; poorly formed server_name directive


The Problem

When you type example.com into the address bar of a browser WITHOUT entering the scheme, i.e. http:// or https://, Nginx redirects the user to https://api.example.com instead of https://example.com as intended. I'm pretty sure there's something wrong with my Nginx config, but I'm not sure what.

Details

I'm hosting two websites on the same server, with the same IP. The relevant bits from the DNS zone file looks something like (domain and IP anonymized here):

example.com. 1800 IN A xxx.xxx.xxx.xxx
www.example.com. 1800 IN CNAME example.com.
api.example.com. 1800 IN CNAME example.com.

I have two SSL certs installed (provided by letsencrypt), one for each site, and both sites are configured to redirect to HTTPS. I have two vhost config files, one for each site, as follows:

  • /etc/nginx/sites-available/api
  • /etc/nginx/sites-available/default

Both are symlinked into /etc/nginx/sites-enabled/. The relevant bits from the two config files are as follows:

# /etc/nginx/sites-available/api
server {
    listen 80;
    listen [::]:80 ipv6only=on;
    server_name api.example.com;
    return 301 https://api.example.com$request_uri;
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    include snippets/api-ssl-params.conf; # ssl config info
    server_name api.example.com;

    # ... the rest of the site config ...
}

and:

# /etc/nginx/sites-available/default
server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}
server {
    listen 443 ssl http2 default_server;
    listen [::]:443 default_server ipv6only=on;
    include snippets/ssl-params.conf; # ssl config info
    server_name example.com;

    # ... the rest of the site config ...
}

I don't understand why just entering example.com into the address bar would redirect to https://api.example.com because:

  1. just plain example.com doesn't appear in the api config file anywhere
  2. example.com shouldn't match the server_name directive api.example.com
  3. the server blocks in default are marked as default_server so shouldn't that take precedence when an ambiguous domain name was typed in?

Thanks!!!


Solution

  • Duh. Figured it out in the process of writing the question. The problem is that just plain example.com doesn't appear in the server_name directive for either of the sites listening on port 80. Since that causes ambiguity, nginx picks the first site in alphabetic order.

    I updated the config file for the default site as follows:

    # /etc/nginx/sites-available/default
    server {
        listen 80;
        listen [::]:80;
        server_name example.com www.example.com; # <-- CHANGED THIS LINE
        return 301 https://example.com$request_uri;
    }
    server {
        listen 443 ssl http2 default_server;
        listen [::]:443 default_server ipv6only=on;
        include snippets/ssl-params.conf; # ssl config info
        server_name example.com;
    
        # ... the rest of the site config ...
    }
    

    And all was right with the universe.