关于在Nginx中配置顶级域名与www域名之间跳转以及https和如何搭配CDN的综合解决方案

方案一方案二(不推荐)方案三
使用场景一般情况顶级域名存在邮件服务顶级域名存在邮件服务
相对复杂度
终点https://www.example.comhttps://www.example.comhttps://www.example.com
隐藏源站IP
相对可用性
缺点无法在顶级域名存在邮件服务的情况下使用无法直接访问https://example.com成本略高,配置麻烦
特殊要求要求DNS服务商支持显性URL转发需要一台代理服务器

方案一

源站配置(以Nginx为例)

关于www和naked域名的重新向以及https的解决方案(以Nginx为例) - 西叉的代码屋 (caoxuan.top)

CDN配置(以腾讯云为例)

基础配置

加速域名www.example.com

源站类型:自有源

回源协议:协议跟随

源站地址:1.1.1.1(源站IP)

回源HOSTwww.example.com

HTTPS配置

强制跳转

跳转配置:开

跳转类型:http->https

跳转方式:301跳转

携带头部:否

DNS配置(以阿里云为例)

记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例;实际以CDN控制台的为准)10分钟
CNAME@默认www.example.com10分钟

路径表

起点过程浏览器请求次数
http://example.com

example.com
起点 -> CDN(301 https://example.com
https://example.com -> CDN -> 源站(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
3
http://www.example.com

www.example.com
起点 -> CDN(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
2
https://example.com起点 -> CDN -> 源站(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
2
https://www.example.com起点 -> CDN -> 源站资源1

应急解决方案(保证可用性;前提:Nginx配置与本方案的源站配置相同)

SSL出现问题

步骤一:修改CDN的HTTPS配置

​ 关闭强制跳转

步骤二:修改 /etc/nginx/conf.d/default.conf

​ 先注释return 301 https://$server_name$request_uri;,然后再将关于资源的配置(比如location, proxy等等)从server4拷贝到server2

​ 示例:

    #server2 http://www.example.com
    server {
        listen       80;
        server_name  www.example.com;
        #return 301 https://$server_name$request_uri;
        location / {
            root   /usr/share/nginx/html;
            index index.html index.htm;
        }
    }

​ 通过以上两步,即可使源站恢复正常工作。

CDN出现问题

步骤一:修改域名解析记录
记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例;实际以CDN控制台的为准)10分钟
CNAME@默认www.example.com10分钟
Awww默认1.1.1.1(源站IP)10分钟

仅需一步,即可使源站恢复正常工作。


方案二

源站配置(以Nginx为例)

关于www和naked域名的重新向以及https的解决方案(以Nginx为例) - 西叉的代码屋 (caoxuan.top)

CDN配置(以腾讯云为例)

基础配置(同方案一)

加速域名www.example.com

源站类型:自有源

回源协议:协议跟随

源站地址:1.1.1.1(源站IP)

回源HOSTwww.example.com

HTTPS配置

强制跳转

跳转配置:开

跳转类型:http->https

跳转方式:301跳转

携带头部:否

DNS配置(以阿里云为例)

记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例;实际以CDN控制台的为准)10分钟
显性URL@默认301 http://www.example.com10分钟

路径表

起点过程浏览器请求次数
http://example.comexample.com起点 -> CDN(301 https://example.com
https://example.com -> CDN -> 源站(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
3
http://www.example.comwww.example.com起点 -> CDN(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
2
https://example.com无法访问0
https://www.example.com起点 -> CDN -> 源站资源1

应急解决方案(保证可用性;前提:Nginx配置与本方案的源站配置相同)

SSL出现问题

步骤一:修改CDN的HTTPS配置

​ 关闭强制跳转

步骤二:修改 /etc/nginx/conf.d/default.conf

​ 先注释return 301 https://$server_name$request_uri;,然后再将关于资源的配置(比如location, proxy等等)从server4拷贝到server2

​ 示例:

    #server2 http://www.example.com
    server {
        listen       80;
        server_name  www.example.com;
        #return 301 https://$server_name$request_uri;
        location / {
            root   /usr/share/nginx/html;
            index index.html index.htm;
        }
    }

通过以上两步,即可使源站恢复正常工作。

CDN出现问题

步骤一:修改域名解析记录
记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例;实际以CDN控制台的为准)10分钟
显性URL@默认301 http://www.example.com10分钟
Awww默认1.1.1.1(源站IP)10分钟

仅需一步,即可使源站恢复正常工作。


方案三

源站配置(以Nginx为例)

关于www和naked域名的重新向以及https的解决方案(以Nginx为例) - 西叉的代码屋 (caoxuan.top)

CDN配置(以腾讯云为例)

基础配置(同方案一)

加速域名www.example.com

源站类型:自有源

回源协议:协议跟随

源站地址:1.1.1.1(源站IP)

回源HOSTwww.example.com

HTTPS配置

强制跳转

跳转配置:开

跳转类型:http->https

跳转方式:301跳转

携带头部:否

DNS配置(以阿里云为例)

记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例值;实际以CDN控制台的为准)10分钟
A@默认0.0.0.0(中转服务器的IP)10分钟

中转服务器配置(以Nginx为例)

vim /etc/nginx/conf.d/default.conf

    #http://example.com
    server {
        listen       80;
        server_name  example.com;
        return 301 http://www.example.com$request_uri;
    }
    #https://example.com
    server {
        listen       443;
        server_name  example.com;
        ssl_certificate 0000000_example.com.pem;
        ssl_certificate_key 0000000_example.com.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_protocols  TLSv1.2 TLSv1.3;
        return 301 https://www.example.com$request_uri;
    }

路径表

起点过程浏览器请求次数
http://example.com

example.com
起点 -> 中转服务器(301 http://www.example.com
http://www.example.com --CDN(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
3
http://www.example.com

www.example.com
起点 -> CDN(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
2
https://example.com起点 -> 中转服务器(301 https://www.example.com
https://www.example.com -> CDN -> 源站资源
2
https://www.example.com起点 -> CDN -> 源站资源1

应急解决方案(保证可用性;前提:Nginx配置与本方案的源站配置相同)

SSL出现问题

步骤一:修改CDN的HTTPS配置

​ 关闭强制跳转

步骤二:修改 /etc/nginx/conf.d/default.conf

​ 先注释return 301 https://$server_name$request_uri;,然后再将关于资源的配置(比如location, proxy等等)从server4拷贝到server2

​ 示例:

    #server2 http://www.example.com
    server {
        listen       80;
        server_name  www.example.com;
        #return 301 https://$server_name$request_uri;
        location / {
            root   /usr/share/nginx/html;
            index index.html index.htm;
        }
    }

通过以上两步,即可使源站恢复正常工作。

CDN出现问题

步骤一:修改域名解析记录
记录类型主机记录解析线路记录值TTL
CNAMEwww默认www.example.com.cdn.dnsv1.com(示例值;实际以CDN控制台的为准)10分钟
A@默认0.0.0.0(中转服务器的IP)10分钟
Awww默认1.1.1.1(源站IP)10分钟

仅需一步,即可使源站恢复正常工作。


鄙人才疏学浅,如有错误,欢迎指出。

以下是nginx的相关配置

#server1 http://example.com
server {
    listen       80;
    server_name  example.com;
    return 301 http://www.example.com$request_uri;
}

#server2 http://www.example.com
server {
    listen       80;
    server_name  www.example.com;
    return 301 https://$server_name$request_uri;
}

#server3 https://example.com
server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate 0000000_example.com.pem;
    ssl_certificate_key 0000000_example.com.key;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols  TLSv1.2 TLSv1.3;
    return 301 https://www.example.com$request_uri;
}

#server4 https://www.example.com
server {
    listen 443 ssl ;
    server_name www.example.com;
    ssl_certificate 0000000_example.com.pem;
    ssl_certificate_key 0000000_example.com.key;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols  TLSv1.2 TLSv1.3;
    location / {
        root   /usr/share/nginx/html;
        index index.html index.htm;
    }    
}

FAQs

Q:为什么要重定向多次?而不直接重定向到 https://www.example.com

A:为了降低出错后的维护成本,当ssl出现问题时,只需修改一个server(server2)即可以http的方式访问,从而恢复正常工作。

Q:这样做对SEO有影响吗?

A:鄙人才疏学浅,请自行查阅资料了解。目前看来,使用Google可以搜索到本站的内容,但是某度却不行。

原因分析:fastjson在反序列化时用的默认使用无参构造方法和getter与setter来构造对象,如果缺少无参构造方法,则会使用当前存在的构造方法(这个构造方法的参数可能不齐全)来构造对象。

解决方案:如果使用了Lombok,就在相应的实体类上添加注解@NoArgsConstructor;否则就手动重写无参构造方法。

package algorithm.loadbalance;

import com.alibaba.fastjson.JSON;

import java.util.*;

/**
 * @author Cao Xuan
 * @date 2021/3/21 20:04
 * @update 2021/5/5
 */
public class LoadBalanceDemo {

    private final List<Server> serverList = new ArrayList<>();

    /**
     * client -> server
     */
    private final Map<Client, Server> clientServerMap = new HashMap<>();
    
    /**
     * <---head---tail--->
     * <---light---weight--->
     * <---former---latter-->
     */
    @Deprecated
    private void sort() {
        for (int i = serverList.size() - 1; i > 0; i--) {
            for (int j = i - 1; j > -1; j--) {
                Server latter = new Server(serverList.get(i));
                Server former = new Server(serverList.get(j));
                if (latter.getCurrentLoad().size() > former.getCurrentLoad().size()) {
                    serverList.set(i, former);
                    serverList.set(j, latter);
                }
            }
        }
        System.out.println("sorting...");
    }

    /**
     * <---head---tail--->
     * <---light---weight--->
     * <---former---latter-->
     */
    private void leafShift() {
        int size = serverList.size();
        for (int i = size - 2; i >= 0; i--) {
            Server latter = new Server(serverList.get(size - 1));
            Server former = new Server(serverList.get(i));
            if (latter.getCurrentLoad().size() > former.getCurrentLoad().size()) {
                serverList.set(size - 1, former);
                serverList.set(i, latter);
                System.out.println("leaf shift");
            }
        }
    }

    private void rightShift(int index) {
        int size = serverList.size();
        for (int i = index + 1; i < size; i++) {
            Server former = new Server(serverList.get(index));
            Server latter = new Server(serverList.get(i));
            if (former.getCurrentLoad().size() < latter.getCurrentLoad().size()) {
                serverList.set(i, former);
                serverList.set(index, latter);
                System.out.println("right shift");
            }
        }
    }
    
    public Server addClient(Client client) {
        if (serverList.size() > 0) {
            Server server = new Server(serverList.get(serverList.size() - 1));
            String serverName = server.getName();
            serverList.get(serverList.size() - 1).updateCurrentLoad(client);
            System.out.println("Hi,[" + client.getUsername() + "]. Assign [" + serverName + "] to you");
            leafShift();
            show();
            return server;
        } else {
            System.err.println("no server");
            return null;
        }
    }

    public void removeClient(String clientName) {
        Server server = clientServerMap.get(new Client(clientName));
        int index = serverList.indexOf(server);
        if (-1 != index) {
            server.currentLoad.remove(new Client(clientName));
            rightShift(index);
            System.out.println("remove a client [" + clientName + "]");
        } else {
            System.err.println("index = -1");
        }

        show();
    }

    public void addServer(String name) {
        Server server = new Server(name, new HashSet<>());
        if (serverList.contains(server)) {
            System.err.println("The server [" + name + "] already exists");
            show();
            return;
        }
        serverList.add(server);
        System.out.println("add a server [" + name + "]");
        show();
    }

    public void removeServer(String serverName) {
        serverList.remove(new Server(serverName));
        System.out.println("add a server [" + serverName + "]");
        show();
    }

    private static class Client {

        private String username;

        public Client(String username) {
            this.username = username;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Client)) {
                return false;
            }
            Client client = (Client) obj;
            return this.getUsername().equals(client.getUsername());
        }

        @Override
        public int hashCode() {
            return Objects.hash(username);
        }

        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
    }

    private static class Server {

        private String name;
        
        private Set<Client> currentLoad;

        public Server(String name) {
            this.name = name;
        }

        public Server(Server server) {
            this.name = server.name;
            this.currentLoad = server.currentLoad;
        }

        public Server(String name, Set<Client> currentLoad) {
            this.name = name;
            this.currentLoad = currentLoad;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Set<Client> getCurrentLoad() {
            return currentLoad;
        }

        public void setCurrentLoad(Set<Client> currentLoad) {
            this.currentLoad = currentLoad;
        }

        public void updateCurrentLoad(Client client) {
            this.currentLoad.add(client);
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Server)) {
                return false;
            }
            Server server = (Server) obj;
            return this.getName().equals(server.getName());
        }

        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
    }

    public static void main(String[] args) {

        LoadBalanceDemo loadBalanceDemo = new LoadBalanceDemo();

        System.out.println("---------------USAGE--------------");
        System.out.println("Add a server: [s+${serverName}]");
        System.out.println("Add a client: [c+${clientName}]");
        System.out.println("Remove a server: [s-${serverName}]");
        System.out.println("Remove a client: [c-${clientName}]");
        System.out.println("Show Current Load: [show]");
        System.out.println("---------------USAGE--------------");

        while (true) {
            System.out.println("ready");
            System.out.println();
            Scanner scanner = new Scanner(System.in);
            String s = scanner.next();
            if ("show".equals(s)) {
                loadBalanceDemo.show();
            } else if (s.startsWith("c+")) {
                String clientName = s.substring(2);
                Client client = new Client(clientName);
                Server server = loadBalanceDemo.addClient(client);
                loadBalanceDemo.clientServerMap.put(client, server);
            } else if (s.startsWith("s+")) {
                String serverName = s.substring(2);
                loadBalanceDemo.addServer(serverName);
            } else if (s.startsWith("c-")) {
                String clientName = s.substring(2);
                loadBalanceDemo.removeClient(clientName);
            } else if (s.startsWith("s-")) {
                String serverName = s.substring(2);
                loadBalanceDemo.removeServer(serverName);
            } else {
                System.out.println("?");
            }
            System.out.println("----------------------------------");
        }
    }

    private void show() {
        System.out.println("current load " + serverList);
    }
}