logo NodeSeekbeta

【进阶向】(含图文)通过Cloudflare的自定义主机名功能进行单域名优选,配合Nginx更深入了解cdn数据流向和SNI分流

一、背景

(一)前言

很多人买了VPS,也用cloudflare大善人提供的服务,同时Nginx是一个比较容易上手的高性能的web服务器,用它来进行反向代理配置,而且还有更好用的Nginx proxy manager配置更是简单(虽然我不用),今天只是通过Nginx来讲解原理,其他的web服务器参考原理来进行配置。
用到cloudflare和nginx常常会有很多需求,譬如:

  1. 隐藏机器IP防止被打;
  2. 加速境内访问自建站点的速度;
  3. 其他用途(各位应该都懂,具体就不展开说了);

(二)开始今天的教程之前,需要做好以下准备:

  1. 拥有一个在cloudflare进行解析的开启了小黄云cdn的域名作为源站域名。(下文用自有域名test.isakura.in来演示)
  2. 已经使用源站域名在自己的服务器通过Nginx部署了一个网站。(最好配置了有效证书)
  3. 拥有一个提供给别人访问的外部域名。(因为我没有多余的域名,我将在cloudflare解析abcd.isakura.in作为我的外部域名,如果有条件的也可以用其他服务商托管解析的域名)
    (也因此本文是单域名优选教程)

(三)最终达成效果:

  1. 浏览器访问https://abcd.isakura.in,最终显示网站的内容;
  2. 浏览器访问https://test.isakura.in,最终返回404;
  3. 优选IP或者域名,加速网站的访问。

(四)通过这次实战你会了解:

  1. CDN数据流向过程;
  2. Nginx中实现的SNI分流;
  3. 数据包传递中请求头host和SNI的作用。

二、引入的前置知识

(一)Nginx的配置文件(每一行代码都有注释)

  1. Nginx的配置文件主要是以代码块的形式存在,主要有全局代码,events块,stream块,和http块,这些块里面又可以含有其他代码块,其中我们需要用的是stream块http块的代码
配置文件总览
#1.部分全局代码
#2.events块
events {
    #主要配置网络连接参数等等内容
}
#3.stream块
stream {
    #主要配置OSI七层模型中传输层的协议(L4)如tcp、udp等等
}
#4.http块
http {
    #主要配置OSI七层模型中应用层的协议(L7)如http
}
  1. 部署一个普通网站的代码块,仅用到http块就好了(已省略不必要参数);
http {
    server {
        listen 80; #监听80端口
        return 301 https://$http_host$request_uri; #收到80端口的http请求重定向为访问443端口的https请求
    }
    server {
        listen  443 ssl; #监听443端口,并启用ssl/tls加密
        server_name test.isakura.in; #监听域名test.isakura.in
        root   /path/to/web; #用于指定网站的资源文件在/path/to/web目录上
        index  index.html index.php; #用于指定网站的默认首页应该读取/path/to/web目录下的index.html或者index.php文件
        location / {
            try_files $uri $uri/ =404;
        }
        #location块是根据请求的URI,匹配相关路径的资源(譬如访问https://test.isakura.in/abcd,就会看location /abcd {}下指定的文件资源,没有就在location / {}下来找)
    }
}	
  1. 通过stream模块来进行域名SNI分流,分流后将请求网站的流量送回http块(已省略不必要参数);
stream {
    # $ssl_preread_server_name是一个内置变量,用于记录进入nginx的流量的SNI值
    map $ssl_preread_server_name $variable {
        abcd.isakura.in abcd;
        test.isakura.in test;
    } 
    #该map块用于映射,用abcd对应代表abcd.isakura.in,用test对应代表test.isakura.in,同时将这两个对应关系记录进变量$variable

    server {
        listen  443  reuseport; #监听443端口,并且进行端口复用
        ssl_preread on; #不解密流量情况下,预先读取SNI值
        proxy_pass $variable; #反向代理,指接收到443端口流量会根据预读取的SNI访问$variable变量指代的值(abcd/test)
    } 
    
    upstream abcd {
        server 127.0.0.1:8080;
    } 
    #用abcd指代服务器 127.0.0.1:8080,也就是访问abcd等于访问127.0.0.1:8080,后续不配置8080端口监听服务了
    upstream test {
        server 127.0.0.1:8081;
    } 
    #用test指代服务器 127.0.0.1:8081,也就是访问abcd等于访问127.0.0.1:8081
}

# http模块用于在stream模块分流后,监听真实的服务,譬如此处监听本机的8081端口,对应test.isakura.in的网站
http {
    server {
        listen 80; #监听80端口
        return 301 https://$http_host$request_uri; #收到80端口的http请求重定向为访问443端口的https请求
    }
    
    server {
        listen  127.0.0.1:8081 ssl; #监听127.0.0.1地址的8081端口,并启用ssl/tls加密
        server_name test.isakura.in; #监听域名test.isakura.in
        root   /path/to/web; #用于指定网站的资源文件在/path/to/web目录上
        index  index.html index.php; #用于指定网站的默认首页应该读取/path/to/web目录下的index.html或者index.php文件
        location / {
            try_files $uri $uri/ =404;
        }
        #location块是根据请求的URI,匹配相关路径的资源(譬如访问https://test.isakura.in/abcd,就会看location /abcd {}下指定的文件资源,没有就在location / {}下来找)
    }
}	

(二)自定义主机名功能(来自SaaS,这个功能的开通需要验证支付方式)

自定义主机名功能中,可以自行定义一个主机名A,当cloudflare接收到访问A的请求时,将这个访问请求回退到一个自定义源服务器B,注意,B的域名必须有托管在cloudflare,进行了解析并开启了小黄云的A记录、AAAA记录、Cname记录。

上面这句话要注意几个点:

  1. B已经是在cloudflare进行了小黄云解析的了,因此B已经隐藏了其真实IP
  2. 需要的是cloudflare接收到访问A主机名的请求,也就是cloudflare的cdn收到了访问A主机名的请求;
  3. 在收到cloudflare的cdn收到请求后,进行回退到源服务器的操作,并且会根据选择的自定义源服务器,回退数据包的SNI值

(三)关于数据包的SNI值和host值

  • 访问网站的数据包在到达服务器时,在传输层使用TCP协议,在应用层使用的是http协议。

  • SNI(Server Name Indication)是tls协议的扩展字段,在https连接的tls握手阶段发送,目的是让服务器在建立加密连接前,就知道客户端想要访问的具体主机名。但如果没有加密连接场景,SNI值则不会存在(此处不考虑该情况)。

  • host值是http 协议的请求头之一,用于在应用层指定客户端想要访问的网站域名。

  • 服务器会先在tls握手时解析SNI值来确立加密连接,随后才会解密http数据,读取host值来处理具体的网页请求。所以从时间顺序上来说,SNI应该是优先被处理,而后续根据http请求头中的host值来访问具体网站内容,因此SNI也充当了路由的功能。这两个值一般来说是一致的,但在优选场景下SNI值出现了变更


(四)优选IP/域名

  • 其实就是在中国大陆访问延迟低,速度快,相对稳定的cloudflare cdn的IP / 解析到cloudflare cdn的IP的域名 / 甚至是反代了cloudflare cdn的ip(譬如reality的IP)(最后这一条我不确保,你们可以试试)

(五)实战前我的基础配置

  • cloudflare配置
    image
    image
  • nginx配置(已部署证书,并略去多余配置)
stream {
    map $ssl_preread_server_name $map {
        test.isakura.in test;
        #其余映射略
    } 
    server {
        listen  443  reuseport; 
        ssl_preread on; 
        proxy_pass $map; 
    } 
    upstream test {
        server 127.0.0.1:8081;
    } 
    upstream others {
        #此处略去其他反代配置
    }
}

http {
    server {
        listen 80; 
        return 301 https://$http_host$request_uri;
    }
    server {
        listen  127.0.0.1:8081 ssl; 
        server_name test.isakura.in; 
        root   /usr/local/testdir; #我的网页存放在/usr/local/testdir
        index  index.html;
        location / {
            try_files $uri $uri/ =404;
        }
        # ssl加密验证配置略,已经配置了证书
    }
}	
  • 网站文件配置
    我的主机在/usr/local/testdir下存放了一个index.html
    image
    它的代码如下(AI写的),访问的时候,会在页面中间显示当前host值。
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>显示 Host 值</title>
    <style>
        body {
            margin: 0;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            font-family: sans-serif;
        }
        h1 {
            margin-bottom: 20px;
        }
        #hostDisplay {
            font-size: 2em;
            color: blue;
        }
    </style>
</head>
<body>
    <h1>当前请求的 Host 是:</h1>
    <div id="hostDisplay"></div>

    <script>
        const host = window.location.host;
        document.getElementById('hostDisplay').textContent = host;
    </script>
</body>
</html>
  • 访问效果如下
    image

三、实战操作(含cloudflare优选,Nginx分流配置,保护源域名)

(一)添加自定义主机名,设置回退到源服务器

原理:这一步的意义是让cloudflare知道,如果我们的访问自定义主机名请求给到cloudflare,那么cloudflare知道要访问哪个源去获取资源

  1. 添加一个默认回退源服务器
    此处我将test.isakura.in作为回退源服务器添加进去,如果B满足我上述“二、(二)”中写的条件,应该添加进去几秒钟后刷新就有效
    image

  2. 添加自定义主机名
    此处是 abcd.isakura.in ,自定义源服务器必须选“自定义源服务器”且需要填回退的源服务器 test.isakura.in
    image
    image
    image

  • 关于自定义源服务器
    a. 默认源服务器:回退到默认源服务器,且SNI值会被设定为“自定义主机名host值”
    b. 自定义源服务器:回退到自定义源服务器,且SNI值会被设定为“自定义源服务器”
    注意,想要为同为cf的域名优选,如果选择默认源服务器,则当回退时,cloudflare会检测到你的自定义主机名所在域是cloudflare管理的域,同时会根据SNI会将请求再次返回自定义主机名,会造成Error 1000,因此如果是在cloudflare中进行优选,则不能选择默认源服务器

  • 关于主机名状态
    如果这个主机名是在cloudflare的顶级域归属cloudflare解析,那么主机名状态会自动变为“有效”,否则,则需要在解析商添加txt验证,此处省略。

  • 关于证书验证方法:
    http验证:是一种自动验证方式,要满足几个条件。首先要满足不是泛域名,然后这个主机名要么是归属cloudflare的,要么它已经由cloudflare来代理流量了;
    txt验证:上述http验证方法不行的情况下,都需要使用txt验证,就是在主机名的解析商添加解析记录进行验证。

  1. txt方式验证证书(有条件的建议使用http验证,此处仅演示)
    image
    image
    过一会然后刷新状态,证书就变为有效了。
    image

(二)为自定义主机名添加dns解析,解析到cloudflare的cdn上。(优选的重要步骤)

原理:虽然cloudflare知道如果它接受到这个访问自定义主机名的请求时,需要访问源服务器获取资源,但这个请求在哪里来呢?因此这一步就是为这个自定义主机名进行解析,让自定义主机名的请求能解析到cloudflare的cdn上这一步也是优选的关键步骤,落到哪个CDN上是由你来定
附:此步骤可以在其他域名解析服务商里面进行,如果在国内的服务商还可以实现国内外线路分流。

  1. 找一个优选的IP/域名
    这个你们自己去找,网上应该很多,我直接就用一个域名了(*.cloudflare.182682.xyz),它含三线解析。

  2. 为自定义主机名添加解析,解析到优选IP/域名上
    image
    如果是优选的域名,也可以添加一个cname记录“cdn.isakura.in”指向“abcd.cloudflare.182682.xyz”,这样以后如果再需要为自定义添加优选域名的话,就可以直接添加cname记录到“cdn.isakura.in”,也方便后续更改优选域名,只要更改cdn.isakura.in的cname记录即可。
    image
    至此,优选已经完成,但是我们访问abcd.isakura.in和test.isakura.in均能访问。
    为了我们的服务器安全,还需要做几步。


(三)在Nginx上配置SNI分流,过滤大部分无效请求

原理:正常访问的http数据包,默认的SNI字段和http请求头中的host是一样的。但经过上面的设置后,访问自定义主机名的请求,最终到达服务器时,host值不改变,SNI值变为源服务器域名

上面已经设置了自定义主机名是 abcd.isakura.in ,源服务器是 test.isakura.in ,因此访问https://abcd.isakura.in 时,当请求来到我的服务器,这个数据的host值为 abcd.isakura.in ,SNI值为 test.isakura.in

因此进行分流需要设置stream块的值,我之前的默认设置已经是配好了,不需要改动
分流的关键设置是在stream中的map块对需要预读的SNI进行限定,这样server块对其进行反向代理时,不属于map块中出现的SNI将会被拒绝。

stream {
    map $ssl_preread_server_name $map {
        test.isakura.in test;
        #其余映射略,在此对需要预读的SNI进行限定
    } 
    server {
        listen  443  reuseport; 
        ssl_preread on; 
        proxy_pass $map; 
    } 
	# 该stream的server块就会默认根据$map变量分流,只需要在上面的map块进行sni配置即可
    upstream test {
        server 127.0.0.1:8081;
    } 
    upstream ...... {
        #此处略去其他反代配置......
    }
}

(四)让Nginx只接受访问自定义主机名的请求,拒绝其他请求

原理:第(三)步已经通过SNI筛选了大部分请求,但是并没有根据host筛选请求。现在需要在http块中设置根据host值判断是否返回内容,如果host值为自定义主机名,则返回内容,否则不返回。

根据host值来进行判断,就是应用层的事情了,在Nginx配置的就是http块,需要在location中添加一段if判断条件的配置,当host不等于abcd.isakura.in时,返回404

http {
    server {
        listen 80; 
        return 301 https://$http_host$request_uri;
    }
    server {
        listen  127.0.0.1:8081 ssl; 
        server_name test.isakura.in; 
        root   /usr/local/testdir; #我的网页存放在/usr/local/testdir
        index  index.html;
        location / {
           try_files $uri $uri/ =404;
				if ($host ! = "abcd.isakura.in") {
					return 404;
				}
				#这里是配置的关键,host值不等于abcd.isakura.in时,返回404
        }
        # ssl加密验证配置略,已经配置了证书
    }
}	

经过以上设置,只有host为abcd.isakura.in的请求能被响应,test.isakura.in的请求失效
image


(五)检查cloudflare加密配置

原理:这里的加密配置主要指的是cloudflare请求我们的网站时是否需要加密(实际上这一步在开启小黄云时就可以设置),有4个选项:
image

  1. 完全严格表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后根据请求的SNI与网站服务器的证书验证有效后使用SSL加密传输(建议用这个,因为既然网站原本就有证书了,就用这个就行);
  2. 完全表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后与网站服务器使用SSL加密传输,证书不一定要验证通过(证书过期、证书名不符等情况均可通过);
  3. 灵活表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后与网站服务器使用http明文传输,无加密。(不建议,不安全)
  4. 关闭表示全链路无加密。

这里要根据服务器部署网站的方式来选择,我个人是选择第一种的,但我建议最起码都要选择第二种。

  • 最后在ITDOG查看一下优选前后的测速状态
  • 这是未优选前的状态
    image
  • 这是优选后的状态
    image

四、结语。

  • 本人作为一个非计算机从业者,上述的原理均为本人自身借助网站资料和AI学习理解,可能真实理解会有偏差,如有错误请纠正支出。
  • 这篇文章,其实是用cloudflare单域名优选的方式,抛转引玉地说明了cdn和web服务器是如何搭配使用的,这不仅仅是在网站CDN部署上,在其他可以发掘的场景也有借鉴之处,希望能帮到有需要的人,也是给我自己留下一个学习记录和第一册测试图床的记录。(另:转载前请注明出处)
123
  • 到我的收藏夹吃灰去吧

  • 教程贴BD

  • 到我的收藏夹吃灰去吧

  • 到我的收藏夹吃灰去吧

  • 先生大义 xhj003 xhj005 xhj003

  • 先收藏再说 xhj003

  • 这个真不错

  • (三)关于数据包的SNI值和host值

    这部分的概念是有误的,可以修订下

  • 现在又不用把ns挂到第三方支持分流的dns提供商了?
    我年初的时候只用cf优选还是会出错的来着。

123

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有61579位seeker

🎉欢迎新用户🎉