前言:
我的博客一直架设在NAS上,但访问非常不方便,每次都要手动输入端口。由于还想搭建其他服务,我就在阿里云买了一个九十九元一年的共享型ECS,配置为2核2G3M。这2G内存除去公摊面积扣掉的400MB,再加上Ubuntu系统运行占用的300MB,基本上1G就没了,真是太可恶了。我计划在上面运行的服务还有alist、ts3、wxchat中转docker镜像、探针以及easyn2n组网联机服务等。
谈到博客,有的用户喜欢动态博客,有的则偏爱静态博客,而我是两者都喜欢用。动态博客最好配合CDN加速,当然也可以选择裸奔。我推荐使用国内VPS搭配阿里云ESA或腾讯云EO的免费CDN使用,对于个人博客来讲,这些免费额度基本够用。这里我先不用ESA了,免得以后麻烦,其实博客程序用谁都一样,主要看个人喜好。
再来讲讲图床,写博客难免会用到图片,然而市面上免费的图床总担心跑路或不稳定,自己用VPS搭建又要额外花钱。所以,我们选择使用Cloudflare R2存储桶进行白嫖。虽然需要绑定外币卡或PayPal才能使用标准版,但它有免费额度,个人使用非常充足。记得先把图片压缩,再通过PicGo上传到R2。虽然很少有人会被恶意刷取R2流量,但为了防止被反向薅羊毛,还是需要设置一下。我是用PayPal绑定R2后解绑了银行卡,所以请各位兄弟姐妹莫要刷我。由于Cloudflare服务器在海外,晚高峰加载速度会非常慢,因此还需要套一层EO的CDN进行加速,并利用边缘函数将其转换为WebP格式,从而实现更快的图片加载速度。
准备工作
记得把博客的评论改为审核模式 还有给博客设置定时备份到云盘 研究Fail2ban给网站设置防扫描爆破
国内VPS1台、CDN EO国内各1个、国内备案域名1个、海外域名1个、 CloudFlare R2
VPS自己买一个大厂国内的,备案域名就不讲了,没有就不套国内CDN裸奔也可必须俩域名 海外域名绑到CF上 开通CF R2即可
EO国内获取地址 这个扫码很好获取激活码,注意别激活到海外去
CloudFlare R2 免费开通测 绑定paypal或者卡开通. 解绑pp卡
1Panel安装以及容器安装
1.1Panel安装
bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)"记得放开防火墙一般都自动放开
一共要安装3个容器 halo博客 数据库postgresql OpenResty
由于我们要方便的管理 我们先安装1panel
登录1Panel网页,安装halo。
「应用商店」-「搜索halo」-「安装」

「数据库服务选择postgresql」-「选择去安装」- 「数据库容器不要勾选端口外部访问」

「halo安装界面勾选端口外部访问」-「确认」

应用商店默认安装openresty即可。

2.测试部署Halo
用vps公网IP+8090,看看可以访问halo博客吗,确认无误后进行下一步。
填写一些基础的账号密码站点名称点击确定没问题我们去接入CDN。

3.VPS基础防护
SSH防护
网页1Panel依次点击
「工具箱」-「Fail2ban」-「全部配置」
如果没安装点击快速安装即可。
输入以下代码保存即可。
#DEFAULT-START
[DEFAULT]
bantime = 600
findtime = 300
maxretry = 5
banaction = ufw
action = %(action_mwl)s
#DEFAULT-END
[sshd]
ignoreip = 127.0.0.1/8
enabled = true
filter = sshd
port = 22
maxretry = 1
findtime = 1d
bantime = -1
banaction = iptables-allports
action = %(action_mwl)s
logpath = /var/log/auth.log开启1panel防火墙
依次点击开启防火墙:
「系统」-「防火墙」
关闭8090端口访问
打开禁止ping

Halo接入EO
1.添加站点
这里要用国内备案域名
点EO国内版进入EO国内版。
依次操作
「输入域名」-「开始接入」

如果没有去上面扫码抽奖。
「绑定至已购套餐」-「免费版」-「我同意协议」-「下一步」

依次点击:
「全球可用区」-「CNAME」-「下一步」

DNS域名解析证明你的归属权,把主机记录和记录值解析完
点击:
「验证」-「完成」

2.博客接入EO
我们先将博客接入EO,后续在把OSS接入EO。
依次点击:
「域名管理」-「加速域名(主域名或二级域名)」-「源站配置选择IP或解析IP的域名」-「模板选择网站加速」-「下一步」

按照图片解析CNAME后,点击完成等待部署即可 。

3.EO的SSL证书和HTTPS配置
我们要配置HTTPS协议和SSL证书。
点击配置

点击边缘HTTPS证书下面的配置

依次点击:
「申请免费证书」-「确认」
等待部署完成。

4.Halo一键部署
Halo可以选择一键部署,和反向代理,我更推荐一键部署。
依次点击或勾选:
「创建网站」-「一键部署」-「输入你的域名」-「端口设置:443」-「勾选SSL」-「启用HTTPS」-「申请证书」-「确认」

依次选择:
「站点加速」-「规则引擎」-「编辑」

如果遇到接入EO 404 就是被扫描爆破的弄得,一堆不存在的php请求导致EO缓存404界面,解决方法如下设置完去EO清除缓存即可。
新增一个IF2 设置URL path 等于 /console/*和/uc/*和/,节点缓存TTL 不缓存,并点击操作添加状态码缓存TTL 选择404 缓存时间0秒 。

如果遇到更新文章cdn还在缓存旧的资源 请去插件中心下载页面静态缓存插件,以及观察图下的cache
eo缓存是否是miss

5.EO网站加速配置
站点加速设置开关
节点缓存 TTL 浏览器缓存 TTL 都是遵循源站 Cache-Control
HTTP/2 回源 打开
OCSP 装订开
网络优化内的HTTP/2 打开
WebSocket 设置300秒打开
CloudFlare R2 存储设置
下面我们就一步一步教大家如何开启 CloudFlare R2 服务:
这里要把你的海外域名托管到Cloudflare上
小建议提示我自己的[可选]CF R2设置屏蔽海外用户和关闭IPV6
1. 创建 R2 存储桶
官方网址:https://www.cloudflare.com/zh-cn/
打开并注册 CF 账户(不是,你不会还没有CF账户吧?😁)进入「R2 对象存储」:

添加支付信息,这里需要一张外币卡 或者 Paypal。
完成之后,就可以「创建存储桶」

点击「创建存储桶」:

存储桶名称:自己填写
位置:亚太地区 或 北美洲西部 (实际速度差不多)
默认存储类:标准(不能选不频繁访问,没有免费额度)

这样就创建完成了!这时候就可以直接在页面上上传和删除等操作。
加你的图床域名,当然主域名要先托管到CF

按需设置CORS 策略等可选:

如果浏览器访问不了在设置这个 在R2设置里面有CORS策略
[
{
"AllowedOrigins": [
"https://你的域名"
],
"AllowedMethods": [
"GET"
]
}
]2. 创建 R2 API
按照下面的路径进行操作,账户API、用户API均可:
「R2对象存储」-「API」-「管理API令牌」-「创建API令牌」

其中权限选择「管理员读和写」,对象读和写也可以,不过要指定桶。

创建好后会出现API密钥等信息,请保存好,以后一些插件、软件都会用到

3.关键防护配置
虽然 R2 基本不怕被刷,但是如果你还有这方面的担忧,可以通过下面三步操作基本杜绝这方面的问题:
1. 设置图片缓存规则
设置这个主要是为了进一步防止被刷下载次数(虽然也基本没人去刷CF的R2),先点进去域名:

然后选择「规则」-「页面规则」-「创建页面规则」:

其中:
URL:https://img.ssqq.de/* 要带https,后面 /*
浏览器缓存 TTL:1天
边缘缓存TTL:1个月(也可以适当降低,如果你经常更换图片的话)
缓存级别:缓存所有内容
源服务器缓存控制:添加但不开启!
这样缓存规则就设置完毕了!
2. 设置速率限制
还通过设置速率限制防止恶意请求:

选择「安全性」-「WAF」-「速率限制规则」-「创建规则」:

其中:
规则名称:随意
字段:URL路径、包含、/
当速率超过...:100,10秒钟
然后采取措施…:阻止
这里重点是【当速率超过...】这个选项,推荐100甚至更多一点,不建议填写太低,很容易误伤;意思是同一个 IP 10 秒内请求超过多少张图片,就触发操作(按照你站点图片情况设置)
3. 设置图片防盗链
这个可以按需添加,主要是防止别的网站盗用你的图床的图片,在别的网址引用图床链接就会提示错误,但是直接请求的方式就还是能打开!
选择「安全性」-「WAF」-「自定义规则」:

主机名:等于,img.ssqq.de (图床域名)
And:右边添加一个 And
引用方:不包含,www.xiaoge.org(你的博客域名)
然后采取措施...:阻止
4.工具链推荐
CloudFlare 的 R2 是兼容 Amazon S3 对象存储的,所以有很多配合的软件可以使用,例如:
picgo-github:https://github.com/Molunerfinn/PicGo
picgo设置
在插件设置中,添加常用插件。
S3插件:用来登录S3的图床
compress-next:用来压缩图片至webp。
watermark:给图片打水印
autoback:用来备份图床
这里有几项配置需要尤其注意。
应用密钥 ID,填写 R2 API 中的 Access Key ID(访问密钥 ID)
应用密钥,填写 R2 API 中的Secret Access Key(机密访问密钥)
桶名,填写 R2 中创建的 Bucket 名称,如创建R2的桶的名字 img
文件路径,上传到 R2 中的文件路径,这里选择使用 {fileName}.{extName} (或者{fullName})来保留原文件的文件名和扩展名。
自定义节点,填写 R2 API 中的「为 S3 客户端使用管辖权地特定的终结点」,即 xxx.r2.cloudflarestorage.com格式的 S3 Endpoint
自定义域名,填写上文生成的https://xxx.r2.dev格式的域名或自定义域名,如我配置的https://img.a.com
ForcePathStyle:no关闭,否则会在最终路径里面显示有桶名。
拒绝无效TLS证书连接 :yes开启,如果出现证书错误可以关闭
ACL访问控制列表:public-read
Bucket前缀:false
URL重写顾名思义 上传后批量替换你的URL为cdn域名推荐piclist更强大支持压缩:https://piclist.cn/
#URL重写顾名思义 上传后批量替换你的URL为cdn域名
#piclist的url重写在 图床-awss3-Default-点击编辑下拉到最下面的自定义域名 就是url重写
piclist 配置
应用密钥 ID,填写 R2 API 中的 Access Key ID(访问密钥 ID)
应用密钥,填写 R2 API 中的Secret Access Key(机密访问密钥)
桶名,填写 R2 中创建的 Bucket 名称,如创建R2的桶的名字 img
文件路径,上传到 R2 中的文件路径,这里选择使用 {fileName}.{extName} (或者{fullName})来保留原文件的文件名和扩展名。我的全在upload/{fullName}
自定义节点,填写 R2 API 中的「为 S3 客户端使用管辖权地特定的终结点」,即 xxx.r2.cloudflarestorage.com格式的 S3 Endpoint
自定义域名,填写上文生成的https://xxx.r2.dev格式的域名或自定义域名,如我配置的https://img.a.com
ForcePathStyle:no关闭,否则会在最终路径里面显示有桶名。
拒绝无效TLS证书连接 :yes开启,如果出现证书错误可以关闭
ACL访问控制列表:public-read
Bucket前缀:false
#图片压缩建议改为75-80 不要改成100那是完全不压缩
ScreenToGif这个自带压缩-官网:https://www.screentogif.com
ScreenToGif-github:https://github.com/NickeManarin/ScreenToGif
图片压缩:https://tinypng.com
CloudFlare-R2-OSS图床接入EO
前提你应该已经把海外域名绑定到了CF上
1.EdgeOne 添加 cdn 域名
1.「新增站点」-输入域名-「开始接入」-去CF-DNS绑定CNAME-「域名管理」

CF解析,证明是你的域名。

点击域名管理

2.配置域名设置回源站点为 R2 的自定义域名站点
记住先择网站加速 自动帮你缓存1个月 免得设置

3.域名服务商配置域名的 cname 指向 EO 的 cname 地址

设置站点加速规则引擎

点击编辑
删除Host里自带的节点缓存TTL

在图片缓存这加一个节点缓存TTL 30天 开启强制缓存
浏览器缓存TTL 设置1小时或1天都可 最好跟你CF 规则页面内的浏览器缓存TTL一致

4.配置 https 证书


此时已经完成了 cdn 的操作,接下来我们配置边缘函数来将 png 转换为 webp 图片减小图片体积
5.图片转换

继续

继续

边缘函数带轻型防盗链
边缘函数-函数管理-编辑代码

覆盖以下代码即可拥有轻度防盗链
把你的域名四个字改为你的博客域名不带http和https等 你的域名写博客地址
async function handleEvent(event) {
const { request } = event;
const url = new URL(request.url);
// --- 1. 防盗链逻辑 (Referer 校验) ---
const referer = request.headers.get('Referer');
// 允许的域名白名单
const allowDomains = ['你的域名'];
// 逻辑:允许空 Referer (直接打开图片);若有 Referer 则必须匹配白名单
let isAuthorized = true;
if (referer) {
try {
const refUrl = new URL(referer);
// 检查 Referer 是否以白名单域名结尾
isAuthorized = allowDomains.some(domain => refUrl.hostname.endsWith(domain));
} catch (e) {
isAuthorized = false;
}
}
// 如果校验不通过,直接响应 403,不再回源
if (!isAuthorized) {
return new Response('Forbidden: Unauthorized Referer', { status: 403 });
}
// --- 2. 图片优化逻辑 ---
const accept = request.headers.get('Accept');
const option = { eo: { image: {} } };
// 检查客户端是否支持 WebP 格式
if (accept && accept.includes('image/webp')) {
option.eo.image.format = 'webp';
}
// 向源站(CF CDN / R2)请求资源
const response = await fetch(request, option);
return response;
}
addEventListener('fetch', event => {
event.passThroughOnException();
event.respondWith(handleEvent(event));
});6.添加触发规则
新增触发规则


注意触发规则写的是eo图床地址,不是博客地址.
EO基础防护
禁止海外访问EO
待定看看会不会影响CF 不影响再发。 CF也可以设置全球禁止只允许China

开启防止盗刷

部署完成。
常见问题
halo导入备份恢复
有的导入超过60MB就会出现上传失败,记得在openresty-性能调整-client_max_body_size
默认60MB根据你的压缩包大小调整最高800MB就是eo的上线 设置完改回60MB
halo批量替换
域名替换
都有过迁移博客要批量替换域名或者图片前缀的问题 halo2版本已经不支持数据库替换了。
如果替换域名请看这篇文章:点我
本地图片替换到对象存储
如果是替换本地的图片到对象存储请看下面的代码 本地图片默认upload文件夹,你需要先把在对象存储内创建upload文件夹,再把图片上传到对象存储的upload文件夹内。
先安装node.js 官网:点我
安装后,先去halo备份下载下来有个extensions.data文件,解压放到桌面新建1文件夹内。然后在1文件夹创建一个xxx.js文件,把下面代码粘贴进去,修改你要替换的对象存储域名域名,在cmd内cd到1文件夹 执行即可 node xxxx.js 他会告诉你替换了多少,把extensions.data文件放入linux系统的vps上 再把workdir文件夹压缩上去再解压出来 extensions.data和workdir文件夹放到一起 cd到目录 用zip -r xxx.zip ./* 来压缩 用halo备份工具恢复即可完成替换。
注意脚本生成的叫extensions-out.data 记得改为extensions.data。
const fs = require('fs');
/* ======================================================
⭐ ① Halo 备份文件路径(必须改对)
------------------------------------------------------
input = 原始 extensions.data
output = 修改后的新文件(恢复用)
Windows 路径注意用 \\
====================================================== */
const inputFilePath = 'C:\\Users\\22265\\Desktop\\1\\extensions.data';
const outputFilePath = 'C:\\Users\\22265\\Desktop\\1\\extensions-out.data';
/* ======================================================
⭐ ② 图片访问域名前缀(最常改的地方)
------------------------------------------------------
例:
https://img.99yu.top
https://cdn.example.com
https://r2.xxx.workers.dev
====================================================== */
const PREFIX = 'https://img.99yu.top';
/* ======================================================
⭐ ③ 图片存放路径规则
------------------------------------------------------
默认是 Halo 的 /upload/ 目录
如果你以后换成 /images/ 或 /assets/
只需要改这里
====================================================== */
const UPLOAD_PATH = '/upload/';
/* ======================================================
⭐ ④ 支持的图片后缀
------------------------------------------------------
想多支持一个就加一个 | 后缀
不要写点 .
====================================================== */
const IMAGE_EXTENSIONS = 'png|jpe?g|webp|gif|svg|avif';
/* ====================== 以下不用改 ===================== */
/**
* base64 解码
*/
function decodeBase64(base64) {
return Buffer.from(base64, 'base64').toString('utf8');
}
/**
* base64 编码
*/
function encodeBase64(str) {
return Buffer.from(str, 'utf8').toString('base64');
}
/**
* 根据上面的可配置项动态生成正则
*/
const IMAGE_REGEX = new RegExp(
`(?<!https?:\\/\\/)(${UPLOAD_PATH.replace(/\//g, '\\/')}[^\\s"'()]+?\\.(${IMAGE_EXTENSIONS}))`,
'gi'
);
// 读取文件
const raw = fs.readFileSync(inputFilePath, 'utf8');
const json = JSON.parse(raw);
let hitCount = 0;
let replaceCount = 0;
// 主处理逻辑
for (const item of json) {
if (!item.data) continue;
const decoded = decodeBase64(item.data);
if (decoded.includes(UPLOAD_PATH)) {
hitCount++;
const replaced = decoded.replace(IMAGE_REGEX, (match) => {
replaceCount++;
return PREFIX + match;
});
if (replaced !== decoded) {
item.data = encodeBase64(replaced);
}
}
}
// 写入新文件
fs.writeFileSync(
outputFilePath,
JSON.stringify(json, null, 2),
'utf8'
);
// 输出结果
console.log(`🔍 命中含图片路径的记录数: ${hitCount}`);
console.log(`🖼️ 实际替换图片引用次数: ${replaceCount}`);
console.log(`🎉 完成: ${outputFilePath}`);
虚拟内存设置
关闭内核节省256m内存
阿里云会扣256m内存
halo博客也不开启端口对外开放
测试完记得进1panel应用商店-已安装内把halo的勾选端口外部访问关闭然后点击重建,目的在于更安全的隐藏源站ip
halo插件
halo邮件设置
halo官方说明:https://docs.halo.run/user-guide/settings/#%E9%82%AE%E4%BB%B6%E9%80%9A%E7%9F%A5
SMTP 主机:smtp.qq.com
SMTP 端口:465
加密方式:SSL
用户名:你的QQ@qq.com
密码:QQ邮箱授权码
发件人地址:你的QQ@qq.com
发件人名称:你的博客名(随便)