16

I am trying to configure my Nginx to strip out www. from hostname. I am looking for generic rule that handle possible subdomain cases.

E.g. If http://www.foo1.sample.com and http://www.fooX2.sample.com are two domains.

Then I want to strip any www. before such subdomains, and redirect them to http://foo1.sample.com and http://fooX2.sample.com respectively (WITHOUT knowing the exact subdomain in the rule.)

Thanks.

lalit
  • 3,283
  • 2
  • 19
  • 26
Tzahi Fridman
  • 320
  • 2
  • 7

2 Answers2

17

Best Practice:

The best practice, as well as the most efficient way, would be to use a separate server definition for this.

This will not only ensure that the config is automatically applied to every single one of the websites that are hosted on your nginx instance, but it'll also makes sure that you don't end up running regular expressions on the hostname from multiple instances in your code.

The following is the simplest form:

server {
    listen      80;
    server_name ~^www\.(?<domain>.+)$;
    return  301 $scheme://$domain$request_uri;
}

If you do use https, then things get more complicated, because you'd have to ensure that the certificates don't mismatch. Unless you have a single certificate for all of your domains, it would be the best practice to simply hardcode everything, as it's already hardcoded in the certificate anyways, and a solution like above simply isn't possible due to the certificate requirements.


Alternative:

Note that the other answer to the question, which uses rewrite ^(.*) http://…$1 …, is incorrect, and will cause you to lose $query_string, as well as potentially mangle the encoding of the request as per Nginx pass_proxy subdirectory without url decoding.

If you require an if-based approach and no hardcoding, neither of which are recommended, e.g., like the other answer, then at least use the correct paradigm, to avoid the bloat and the loss of the $query_string; note that as per nginx server name regex when "Host" header has a trailing dot, the $host variable is already normalised by nginx (trailing dot removed, whole thing brought to lower case), so, you don't need to worry about doing a case-insensitive comparison like in the other answer, either:

if ($host ~ ^www\.(?<domain>.+)$) {
    return  301 $scheme://$domain$request_uri;
}

References:

cnst
  • 25,870
  • 6
  • 90
  • 122
  • I was planning on just awarding it to the original answer, because it worked for me, but your answer is clearly more detailed and lays out the options really well, so kudos! – CorayThan Jan 21 '19 at 18:17
  • @CorayThan, thanks! BTW, usually, the community examines all questions on the bounty in the final day/hours of the bounty, so, it's best to not award it until the end to get more upvotes for good questions and answers, but I'm glad you got an improved answer out of it anyways! – cnst Jan 21 '19 at 20:41
14

I think adding following If block in your Nginx conf file should work for you. It also takes care of subdomain case you mentioned.

if ($host ~* ^www\.(.*)) {       
    set $host_without_www $1;
    rewrite ^(.*) http://$host_without_www$1 permanent;
}
lalit
  • 3,283
  • 2
  • 19
  • 26
  • 1
    you are the best .. it's working like charm. I've been looking for this lines of code exactly .. Thank you for your time. – Saud Alfadhli Jun 25 '15 at 03:12
  • Sorry, but can please anyone explain this: What does `~*` mean? Why is `$1` used after `$host_wthout_www`? – user3563059 Jul 07 '18 at 01:49
  • The `~` (tilda) is a regular expression match operator. The asterisk after it makes it case insensitive. The `$1` means the match from the first block in parenthesis. – obaranovsky Aug 05 '20 at 19:47