Utilize Nginx to fulfill 301 redirect to root domain of https
Tags: nginx, http, regex
Published: 2020-05-23 02:23:42

Base on consideration of SEO and security, the redirection should be 301, which will be handled generally via Nginx
<!-- more -->


## Result to Accomplish

The following address need to be redirected to the root domain of https https://chanvinxiao.com
- http://chanvinxiao.com （http without www）
- http://www.chanvinxiao.com （http with www）
- https://www.chanvinxiao.com （https with www）

## Different between 301 and 302

[301][mdn-301] is to redirect permanently, [302][mdn-302] is to redirect temporary, and the major distinction is lay in the handling way of search engine
- 301：search engine will transfer weight and PR value
- 302：search engine will not handle additionally

Now I want search engine treat the original url as non-existence anymore, and completely shift to the new address, so 301 will be used


## Redirect Http to Https

The simplest way is to `return` a redirected address, and add a `301` status code in the middle (or else default 302 will be supposed)
```
server {
  listen 80;
  return 301 https://$host$request_uri;
}
```
- [return][nginx-return] and [rewrite][nginx-rewrite] are both rewrite directive of Nginx, since here the path don't need to be changed, `return` will be more convenient
- [$host][nginx-host] and [$request_uri][nginx-request_uri] are both Nginx http module's embedded variables, the combination of these two parameters is equivalent to getting rid of `http://` for request url


##  Www Redirect to Root Domain 

Only need to handle in https, because all `http` has been redirected to `https`
```
server {
  listen 443 ssl;
  server_name ~^(?<www>www\.)?(.+)$;
  if ( $www ) {
    return 301 https://$2$request_uri;
  }
...
```
- Regular Expression matching of [server_name][nginx-server_name] have been utilized, which can been enabled via adding `~` in front of its value, and [PCRE][pcre] grammar is supported
- Regex usage is to determine if there's prefix `www`, and to capture root domain, which will generate two variable, one is [name capture][pcre-named_capture] variable `$www`, the other is numeric capture variable $2
- numeric capture variable is not supported in `if`, or else there will be error thrown (`unknown "1" variable`), that's why `?<www>` is added to assign `$1` to `$www`


## Reduce Redirect Times

The result is fulfilled by the above settings, but there's a flaw: http://www.chanvinxiao.com will redirect to https://www.chanvinxiao.com firstly, then redirect again to https://chanvinxiao.com. redirecting twice is definitely not as good as redirecting once, so one-stop would be better, and the http config will be modified as follow:
```
server {
  listen 80;
  server_name ~^(?:www\.)?(.+)$;
  return 301 https://$1$request_uri;
}
```
- In the corresponding server of http, `server_name` also is changed to regex mode, and $host is substituted with captured root domain `$1` 
- Www will be removed directly, so it's not necessary to capture, use `?:` symbol to fulfill [grouping][pcre-groups] without capturing, so the following root domain becomes `$1`
- Thus the result is that no matter there is www or not, it will be redirected to https domain without www


## Summary

There is not need to appoint specific domain in the above config, so it will be convenient to be compatible and migrate, and the following feature of Nginx is used: 
- Regular expression matching of `server_name`
- `return` directive which receive status code and address
- `$host` and `$request_uri` embedded variables



[mdn-301]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301
[mdn-302]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302
[nginx-return]: http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#return
[nginx-rewrite]: http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite
[nginx-host]: http://nginx.org/en/docs/http/ngx_http_core_module.html#var_host
[nginx-request_uri]: http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri
[nginx-server_name]: http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
[pcre]: http://www.pcre.org
[pcre-named_capture]: http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC16
[pcre-groups]: http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC14