一、背景
(一)前言
很多人买了VPS,也用cloudflare大善人提供的服务,同时Nginx是一个比较容易上手的高性能的web服务器,用它来进行反向代理配置,而且还有更好用的Nginx proxy manager配置更是简单(虽然我不用),今天只是通过Nginx来讲解原理,其他的web服务器参考原理来进行配置。
用到cloudflare和nginx常常会有很多需求,譬如:
- 隐藏机器IP防止被打;
- 加速境内访问自建站点的速度;
- 其他用途(各位应该都懂,具体就不展开说了);
(二)开始今天的教程之前,需要做好以下准备:
- 拥有一个在cloudflare进行解析的开启了小黄云cdn的域名作为源站域名。(下文用自有域名test.isakura.in来演示)
- 已经使用源站域名在自己的服务器通过Nginx部署了一个网站。(最好配置了有效证书)
- 拥有一个提供给别人访问的外部域名。(因为我没有多余的域名,我将在cloudflare解析abcd.isakura.in作为我的外部域名,如果有条件的也可以用其他服务商托管解析的域名)
(也因此本文是单域名优选教程)
(三)最终达成效果:
- 浏览器访问https://abcd.isakura.in,最终显示网站的内容;
- 浏览器访问https://test.isakura.in,最终返回404;
- 优选IP或者域名,加速网站的访问。
(四)通过这次实战你会了解:
- CDN数据流向过程;
- Nginx中实现的SNI分流;
- 数据包传递中请求头host和SNI的作用。
二、引入的前置知识
(一)Nginx的配置文件(每一行代码都有注释)
- Nginx的配置文件主要是以代码块的形式存在,主要有全局代码,events块,stream块,和http块,这些块里面又可以含有其他代码块,其中我们需要用的是stream块和http块的代码
配置文件总览
#1.部分全局代码
#2.events块
events {
#主要配置网络连接参数等等内容
}
#3.stream块
stream {
#主要配置OSI七层模型中传输层的协议(L4)如tcp、udp等等
}
#4.http块
http {
#主要配置OSI七层模型中应用层的协议(L7)如http
}
- 部署一个普通网站的代码块,仅用到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 / {}下来找)
}
}
- 通过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记录。
上面这句话要注意几个点:
- B已经是在cloudflare进行了小黄云解析的了,因此B已经隐藏了其真实IP;
- 需要的是cloudflare接收到访问A主机名的请求,也就是cloudflare的cdn收到了访问A主机名的请求;
- 在收到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配置


- 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

它的代码如下(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>
- 访问效果如下

三、实战操作(含cloudflare优选,Nginx分流配置,保护源域名)
(一)添加自定义主机名,设置回退到源服务器
原理:这一步的意义是让cloudflare知道,如果我们的访问自定义主机名请求给到cloudflare,那么cloudflare知道要访问哪个源去获取资源
-
添加一个默认回退源服务器
此处我将test.isakura.in作为回退源服务器添加进去,如果B满足我上述“二、(二)”中写的条件,应该添加进去几秒钟后刷新就有效

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



-
关于自定义源服务器
a. 默认源服务器:回退到默认源服务器,且SNI值会被设定为“自定义主机名host值”
b. 自定义源服务器:回退到自定义源服务器,且SNI值会被设定为“自定义源服务器”
注意,想要为同为cf的域名优选,如果选择默认源服务器,则当回退时,cloudflare会检测到你的自定义主机名所在域是cloudflare管理的域,同时会根据SNI会将请求再次返回自定义主机名,会造成Error 1000,因此如果是在cloudflare中进行优选,则不能选择默认源服务器 -
关于主机名状态
如果这个主机名是在cloudflare的顶级域归属cloudflare解析,那么主机名状态会自动变为“有效”,否则,则需要在解析商添加txt验证,此处省略。 -
关于证书验证方法:
http验证:是一种自动验证方式,要满足几个条件。首先要满足不是泛域名,然后这个主机名要么是归属cloudflare的,要么它已经由cloudflare来代理流量了;
txt验证:上述http验证方法不行的情况下,都需要使用txt验证,就是在主机名的解析商添加解析记录进行验证。
- txt方式验证证书(有条件的建议使用http验证,此处仅演示)


过一会然后刷新状态,证书就变为有效了。

(二)为自定义主机名添加dns解析,解析到cloudflare的cdn上。(优选的重要步骤)
原理:虽然cloudflare知道如果它接受到这个访问自定义主机名的请求时,需要访问源服务器获取资源,但这个请求在哪里来呢?因此这一步就是为这个自定义主机名进行解析,让自定义主机名的请求能解析到cloudflare的cdn上。这一步也是优选的关键步骤,落到哪个CDN上是由你来定。
附:此步骤可以在其他域名解析服务商里面进行,如果在国内的服务商还可以实现国内外线路分流。
-
找一个优选的IP/域名
这个你们自己去找,网上应该很多,我直接就用一个域名了(*.cloudflare.182682.xyz),它含三线解析。 -
为自定义主机名添加解析,解析到优选IP/域名上

如果是优选的域名,也可以添加一个cname记录“cdn.isakura.in”指向“abcd.cloudflare.182682.xyz”,这样以后如果再需要为自定义添加优选域名的话,就可以直接添加cname记录到“cdn.isakura.in”,也方便后续更改优选域名,只要更改cdn.isakura.in的cname记录即可。

至此,优选已经完成,但是我们访问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的请求失效

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

- 完全严格表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后根据请求的SNI与网站服务器的证书验证有效后使用SSL加密传输(建议用这个,因为既然网站原本就有证书了,就用这个就行);
- 完全表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后与网站服务器使用SSL加密传输,证书不一定要验证通过(证书过期、证书名不符等情况均可通过);
- 灵活表示你与cloudflare之间使用cloudflare证书加密连接,而cloudflare在收到请求后与网站服务器使用http明文传输,无加密。(不建议,不安全)
- 关闭表示全链路无加密。
这里要根据服务器部署网站的方式来选择,我个人是选择第一种的,但我建议最起码都要选择第二种。
- 最后在ITDOG查看一下优选前后的测速状态
- 这是未优选前的状态

- 这是优选后的状态

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

@枪王101 #4
个个都是吃灰啊。。。
先收藏再说
这个真不错
这部分的概念是有误的,可以修订下
现在又不用把ns挂到第三方支持分流的dns提供商了?
我年初的时候只用cf优选还是会出错的来着。