Discover Centmin Mod today
Register Now

Nginx Nginx Config... I think I made a big mistake!

Discussion in 'Nginx, PHP-FPM & MariaDB MySQL' started by deltahf, Nov 8, 2016.

  1. deltahf

    deltahf Premium Member Premium Member

    587
    265
    63
    Jun 8, 2014
    Ratings:
    +489
    Local Time:
    1:27 PM
    It was brought to my attention today that my site is being served at multiple URLs (horrifying, as this is something I thought I had protected against). I suspect the problem began when I moved my site to SSL last year. Now, my site (the only site configured on my server at port 443) is being delivered via HTTPS via all of the domains which point to my server (and yes... Chrome does throw a domain/certificate mis-match warning when you attempt to access them).

    Basically, my site should only be served at https://www.example.net. Any other requests should be redirected to that domain via 301 redirect, so https://example.net and the non-HTTPS variants like http://www.example.net and http://example.net should all 301 to the official domain.

    I also own example.com, which many people mistakenly type in, so these redirects need to work as well to send visitors to example.net.

    When making my initial configuration, I had not considered what happens if you access the above domains with the HTTPS protocol. So, if you access http://example.com or http://www.example.com or http://example.net, they all redirect exactly as they should. However, if you access any of those domains via https://, there is no redirect and the entire site is accessible on the domain.

    Here's my current Nginx server configurations:

    Code (Text):
    # REDIRECT OTHER DOMAINS
    server {
       listen   80;
       server_name   example.net www.example.net example.com www.example.com;
       return   301 https://www.example.net$request_uri;
    }
    
    # PRIMARY SITE CONFIG
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       ...
    }
    


    After reviewing the Nginx documentation again, I think I know what is happening now: all the HTTPS requests coming in over port 443 are, per Nginx default rules, being served by the second server block, because it is the only one configured to listen on that port. I had been under the impression that it would only serve content if the request matched the server_name, but clearly that was wrong.

    Now I am left with a few questions on how to fix this mess. First: is it possible to configure HTTPS redirects without having SSL certificates for each of the domains? In other words, can I redirect https://www.example.com to https://www.example.net without buying another SSL certificate for example.com?

    I presume the answer to that question is "no", which leads me to the question about what to do next. What is the best practice for handling this situation?

    Should I create another server block to act as the default server for requests on 443 which returns the Nginx non-standard 444 HTTP error code (it's explained in the docs here)? That would give me something like this:

    Code (Text):
    # REDIRECT OTHER DOMAINS
    server {
       listen   80;
       server_name   example.net www.example.net example.com www.example.com;
       return   301 https://www.example.net$request_uri;
    }
    
    # HTTPS DEFAULT SERVER
    server {
       listen   443 ssl http2;
       server_name   example.com www.example.com;
       return   444;
    }
    
    # PRIMARY SITE CONFIG
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       ...
    }
    



    I'm not sure how to handle this. Any advice would be appreciated!
     
    Last edited: Nov 8, 2016
  2. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    yeah each domain will have to have it's name listed in a SSL certificate otherwise it won't work and get browser errors

    so separate nginx vhosts for each different domain extension .com and .net

    But it's much simpler from SEO perspective.

    If you server both https and http version of your sites or have mirror sites or domains serving same content, you need to tell search engines which is the preferred url for SEO/indexing otherwise search engines see it as duplicate content.

    Just add canonical headers to your nginx vhost files for /usr/local/nginx/conf/conf.d/domain.com.conf (and if applicable /usr/local/nginx/conf/conf.d/domain.com.ssl.conf) and to static file location context for html/htm within the /usr/local/nginx/conf/staticfiles.conf include file in your nginx vhosts. This is to ensure the preferred and right url is picked up for search engine indexing.

    Example for for my centminmod.com site my preferred index url is non-https one for https so my nginx canonical header to add is
    Code (Text):
    add_header Link "<http://centminmod.com$request_uri>; rel=\"canonical\"";
    

    centminmod.com access check using curl header check
    Code (Text):
    curl -Is http://centminmod.com | grep canonical
    Link: <http://centminmod.com/>; rel="canonical"

    centmin.com is a mirror site and access check using curl header check so canonical header pointing to my http version of centminmod.com which is my preferred main domain url for SEO
    Code (Text):
    curl -Is http://centmin.com | grep canonical
    Link: <http://centminmod.com/>; rel="canonical"
    

    Detail & Info
    But yes, separate nginx vhosts for each different domain extension .com and .net would still be needed for HTTPS port 443
     
  3. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    ah re-read your problem, while nginx canonical headers help for SEO, for end user experience i guess non-HTTPS domains need to just have their own server{} contexts separate from the https one with 301 redirect

    so believe something like below if you want separate ssl certs for .com and .net

    Code (Text):
    # REDIRECT OTHER DOMAINS COM
    server {
       listen   80;
       server_name  example.com www.example.com;
       return   301 https://www.example.com$request_uri;
    }
    
    # REDIRECT OTHER DOMAINS NET
    server {
      listen   80;
       server_name   example.net www.example.net;
       return   301 https://www.example.net$request_uri;
    }
    
    # PRIMARY SITE CONFIG COM
    server {
       listen   443 ssl http2;
       server_name   www.example.com;
       add_header Link "<http://www.example.net$request_uri>; rel=\"canonical\"";
       ...
    }
    
    # PRIMARY SITE CONFIG NET
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       add_header Link "<http://www.example.net$request_uri>; rel=\"canonical\"";
       ...
    }


    don't need 444 returns
     
  4. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    and if you want .com redirected to .net still need separate ssl cert for both domains but can do

    Code (Text):
    # REDIRECT OTHER DOMAINS COM
    server {
       listen   80;
       server_name  example.com www.example.com;
       return   301 https://www.example.com$request_uri;
    }
    
    # REDIRECT OTHER DOMAINS NET
    server {
      listen   80;
       server_name   example.net www.example.net;
       return   301 https://www.example.net$request_uri;
    }
    
    # PRIMARY SITE CONFIG COM
    server {
       listen   443 ssl http2;
       server_name   www.example.com;
       return   301 https://www.example.net$request_uri;
       ...
    }
    
    # PRIMARY SITE CONFIG NET
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       add_header Link "<http://www.example.net$request_uri>; rel=\"canonical\"";
       ...
    }
     
  5. dorobo

    dorobo Active Member

    420
    104
    43
    Jun 6, 2014
    Ratings:
    +162
    Local Time:
    1:27 AM
    latest
    latest
    I tried requesting all my non ssl domains via https on chrome and at first I see the usual

    Your connection is not private and that the certificate that it's using is the domain with valid certificate on it.

    If I click on the link at the bottom that says proceed, sure enough it's the SSL domain.

    I do have 2 domains in there that has SSL but only one has this line - add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;"; commented out and that's where the non-SSL domains get directed to if I force them to go https.
     
  6. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    yeah that's normal, if you want you can setup separate https server{} context for each non-https domains and do a return 301/302 redirect to non-https, http domain

    Using the same example domains if example.com doesn't have https and only http
    Code (Text):
    # main non-https domain
    server {
       listen   80;
       server_name  example.com www.example.com;
    }
    
    # redirect https to non-https which doesn't need ssl cert
    server {
       listen   443 ssl http2;
       server_name  example.com www.example.com;
       return   301 http://www.example.com$request_uri;
       ...
    }
     
  7. deltahf

    deltahf Premium Member Premium Member

    587
    265
    63
    Jun 8, 2014
    Ratings:
    +489
    Local Time:
    1:27 PM
    Thanks for your help, @eva2000! After thinking it over and doing more analysis, I think I just want to send all the other domains to a dead end when someone tries to access them via https://. I doubt any normal person would type out the "https" if they try to visit my website, and I don't think there are any important backlinks to the incorrect domains out there. I really just want to get the duplicate content removed from Google, and to ensure this cannot happen again.

    Would something like this be the best way to achieve that?

    Code (Text):
    # REDIRECT OTHER DOMAINS
    server {
      listen   80;
       server_name   example.net www.example.net example.com www.example.com;
       return   301 https://www.example.net$request_uri;
    }
    
    # TERMINATE ALL INCORRECT HTTPS REQUESTS
    server {
       listen   443 ssl http2;
       server_name   example.net example.com www.example.com;
       return   404;
    }
    
    # PRIMARY SITE CONFIG
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       ...
    }
    


    *** EDIT/UPDATE ***

    Nothing seems to be working, including this above — it's catching all of the normal requests to my site and makes it return 404s as well.

    I just want to deny all 443/ssl/https requests which are not to www.example.net by default; that's it.

    Yeah, I suspect that many people might have this problem without realizing it. I probably just noticed it because I run a ".net" website with ".com" redirects.
     
    Last edited: Nov 8, 2016
  8. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    Setting up canonical headers is the best way to do that for SEO side

    For nginx redirect side probably closest would be this method outlined here.
     
  9. deltahf

    deltahf Premium Member Premium Member

    587
    265
    63
    Jun 8, 2014
    Ratings:
    +489
    Local Time:
    1:27 PM
    OK, so this is a much more complicated issue than I originally thought. Apparently, configuring "default" behavior for Nginx sites using SSL is quite different from that of non-SSL sites.

    I see three practical options:

    Option 1: Create a self-signed certificate and use it to handle unwanted SSL requests. This is explained in more detail here: Nginx: rejecting unknown or specific domains over SSL

    Option 2: Use an "if" conditional in the appropriate server block to check to see if the $host variable from the request matches the defined $server_name. This is suggested and explained in the following answer on StackOverflow: ssl - Properly setting up a "default" nginx server for https - Server Fault

    Option 3: Host the SSL site on a dedicated IP address that is not used for any other domains.

    I have decided to proceed with Option #2, and it is working as expected. I do have some performance concerns about using an "if" conditional statement in my server block, but these are probably overblown (I hope), and it is the fastest/easiest option for me to implement and maintain, which is worth a lot.

    If any of you have any concerns about using "if" in my server block this way, please let me know. My Nginx configuration for the SSL site looks something like this now:

    Code (Text):
    server {
       listen   443 ssl http2;
       server_name   www.example.net;
       
       if ($host != $server_name)
       {
          return 444;
       }
       
       ...
    }
    


    I have also decided to offload the non-SSL redirects from other domain names to my domain registrar, NameSilo. They offer path forwarding, so they will be handling all of my example.com and example.com/whatever-path redirects now, while blocking https requests, and I won't have those old rules cluttering up my Nginx config files. :)
     
  10. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    Yeah if method would work and DNS level forwarding instead of web server nginx level forwarding is also an option. But remember canonical header is a must if you have more than one domain serving the same content.
     
  11. deltahf

    deltahf Premium Member Premium Member

    587
    265
    63
    Jun 8, 2014
    Ratings:
    +489
    Local Time:
    1:27 PM
    Yeah, for sure. In my case, though, I only want to serve everything exclusively, from one domain, via SSL.

    Surprisingly, that proved to be the more difficult configuration! :confused:
     
  12. eva2000

    eva2000 Administrator Staff Member

    55,189
    12,251
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,829
    Local Time:
    3:27 AM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    well your requirements for multiple non-https domains to be redirected to primary domain inspired me to add a manual step mode to addons/acmetool.sh in 123.09beta01 branch so you can manually get free letsencrypt ssl certificates for each domain too Beta Branch - acmetool.sh 1.0.17 manual steps mode added and then redirect from their respective own domain.com.ssl.conf and domain.com.conf nginx vhost files.
     
  13. deltahf

    deltahf Premium Member Premium Member

    587
    265
    63
    Jun 8, 2014
    Ratings:
    +489
    Local Time:
    1:27 PM
    Wow, that's great. Very cool! :D