Redirect www to not-www

P
  • 10 Jan '23
Happy 2023 to all on this list.

Using nginx (1.18.0 on Ubuntu 20.04.5) as proxy to back-end, I have 
three sites (a|b|c.example.com) in a fast, reliable production 
environment. I have DNS records set up for www.a|b|c.example.com.  I 
have CertBot set up for only a|b|c.example.com.

To avoid "doubling" the number of sites-available and security scripts, 
and to avoid the unnecessary "www." I would like to add something like:

server {
   server_name www.a.example.com;
   return 301 $scheme://a.example.com$request_uri;
}

but I have tried this in several places, www.a.example.com works, but 
does not remove the www prefix, and fails any browser's security checks 
(nginx -t is "ok").

Where, in the following config, is the most elegant place to put such a 
"return" line?  Maybe I'm missing something fundamental?

server {
     listen 443 ssl;
                      [ ... # 4 lines managed by Certbot ... ]
     server_name a.example.com;    # Note: or b.example.com, or 
c.example.com
                      [ ... logging ... ]
     proxy_buffering off;
     if ($request_method !~ ^(GET|HEAD|POST)$) {
        return 444;
     }
     location / {
         proxy_pass http://192.168.x.y:port;
         proxy_set_header Host $host;
         proxy_http_version 1.1;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
server {
     if ($host = a.example.com) {    # Note: or b.example.com, or 
c.example.com
         return 301 https://$host$request_uri;
     }
     listen 80;
     server_name a.example.com;      # Note: or b.example.com, or 
c.example.com
     rewrite     ^   https://$host$request_uri? permanent;
}

Many thanks -- Paul

   \\\||//
    (@ @)
ooO_(_)_Ooo__________________________________
|______|_____|_____|_____|_____|_____|_____|_____|
|___|____|_____|_____|_____|_____|_____|_____|____|
|_____|_____| mailto:paul at stormy.ca _|____|____|
F
  • 10 Jan '23
On Tue, Jan 10, 2023 at 12:03:06PM -0500, Paul wrote:

Hi there,

> Using nginx (1.18.0 on Ubuntu 20.04.5) as proxy to back-end, I have three
> sites (a|b|c.example.com) in a fast, reliable production environment. I have
> DNS records set up for www.a|b|c.example.com.  I have CertBot set up for
> only a|b|c.example.com.
> 
> To avoid "doubling" the number of sites-available and security scripts, and
> to avoid the unnecessary "www." I would like to add something like:
> 
> server {
>   server_name www.a.example.com;
>   return 301 $scheme://a.example.com$request_uri;
> }

> Maybe I'm missing something fundamental?

Yes, you are missing something fundamental :-(

There are 4 families of requests that the client can make:

* http://www.a.example.com
* http://a.example.com
* https://www.a.example.com
* https://a.example.com

It looks like you want each of the first three to be redirected to
the fourth?

It is straightforward to redirect the first two to the fourth --
something like

    server {
        server_name a.example.com www.a.example.com;
        return 301 https://a.example.com$request_uri;
    }

should cover both.

(Optionally with "listen 80;", it replaces your similar no-ssl server{}
block.)

But for the third family, the client will first try to validate the
certificate that it is given when it connects to www.a.example.com,
before it will make the http(s) request that you can reply to with
a redirect. And since you do not (appear to) have a certificate for
www.a.example.com, that validation will fail and there is nothing you
can do about it. (Other that get a certificate.)

Cheers,

    f
-- 
Francis Daly        francis at daoine.org
P
  • 10 Jan '23
On 2023-01-10 13:43, Francis Daly wrote:

>> Using nginx (1.18.0 on Ubuntu 20.04.5) as proxy to back-end, I have three
>> sites (a|b|c.example.com) in a fast, reliable production environment. I have
>> DNS records set up for www.a|b|c.example.com.  I have CertBot set up for
>> only a|b|c.example.com.
>>
>> To avoid "doubling" the number of sites-available and security scripts, and
>> to avoid the unnecessary "www." I would like to add something like:
>> /.../
> There are 4 families of requests that the client can make:
> 
> * http://www.a.example.com
> * http://a.example.com
> * https://www.a.example.com
> * https://a.example.com
> 
> It looks like you want each of the first three to be redirected to
> the fourth?

Many thanks.  That is totally correct.  Given your comment re "lack of 
certificate" and "validation will fail"  I have now expanded CertBot to 
include the three "www." names. All works fine (as far as I can see 
using Firefox, Opera, Vivaldi clients -- and Edge, had to boot up an old 
laptop!)

BUT... for that one step further and have all server (nginx) responses 
go back to the end-client as:
     https://a.example.com
and NOT as:
     https://www.a.example.com
             ^^^
I have written an /etc/nginx/conf.d/redirect.conf as:
server {
   server_name www.a.example.com;
   return 301 $scheme://a.example.com$request_uri;
}

which seems to work, but I would appreciate your opinion - is this the 
best, most elegant, secure way?  Does it need "permanent" somewhere?

I've never used "scheme" before today, but we've got an external 
advisory audit going on, and I'm trying to keep them happy.

Many thanks and best regards,
Paul

> 
> It is straightforward to redirect the first two to the fourth --
> something like
> 
>   server {
>       server_name a.example.com www.a.example.com;
>       return 301 https://a.example.com$request_uri;
>   }
> 
> should cover both.
> 
> (Optionally with "listen 80;", it replaces your similar no-ssl server{}
> block.)
> 
> But for the third family, the client will first try to validate the
> certificate that it is given when it connects to www.a.example.com,
> before it will make the http(s) request that you can reply to with
> a redirect. And since you do not (appear to) have a certificate for
> www.a.example.com, that validation will fail and there is nothing you
> can do about it. (Other that get a certificate.)
> 
> Cheers,
> 
>   f

   \\\||//
    (@ @)
ooO_(_)_Ooo__________________________________
|______|_____|_____|_____|_____|_____|_____|_____|
|___|____|_____|_____|_____|_____|_____|_____|____|
|_____|_____| mailto:paul at stormy.ca _|____|____|
F
  • 11 Jan '23
On Tue, Jan 10, 2023 at 06:45:15PM -0500, Paul wrote:

Hi there,

> BUT... for that one step further and have all server (nginx) responses go
> back to the end-client as:
>     https://a.example.com
> and NOT as:
>     https://www.a.example.com
>             ^^^
> I have written an /etc/nginx/conf.d/redirect.conf as:
> server {
>   server_name www.a.example.com;
>   return 301 $scheme://a.example.com$request_uri;
> }
> 
> which seems to work, but I would appreciate your opinion - is this the best,
> most elegant, secure way?  Does it need "permanent" somewhere?

It does not need "permanent" -- that it a signal to "rewrite" to use a http
301 not http 302 response; and you are using a http 301 response directly.

(See, for example, http://http.cat/301 or http://http.cat/302 for the
meaning of the numbers. Warning: contains cats.)

> I've never used "scheme" before today, but we've got an external advisory
> audit going on, and I'm trying to keep them happy.

$scheme is http or https depending on the incoming ssl status. That 4-line
server{} block does not do ssl, so $scheme is always http there. 

http://nginx.org/r/$scheme

Either way, this would redirect from http://www.a. to http://a., and
then the next request would redirect from http://a. to https://a.. I
suggest that you are better off just redirecting to https the first time.

You will want a server{} with something like "listen 443 ssl;" and
"server_name www.a.example.com;" and the appropriate certificate and key;
and then also redirect to https://a. in that block.

So for the four families http,https of www.a,a you will probably want
three or four server{} blocks -- you could either put http www.a and
http a in one block; or you could put https www.a and http www.a in one
block; and then one block for the other, plus one for the https a that
is the "real" config -- the other ones will be small enough configs that
"just" return 301 to https://a. Which should be simple enough to audit
for correctness.

Good luck with it,

    f
-- 
Francis Daly        francis at daoine.org