2026-02-26 14:11:43
一开始打算使用 web 来做的,但是发现浏览器没有那么高的权限,限制来自浏览器安全模型。浏览器不允许:创建任意 TCP Socket,主动向任意 IP:PORT 建立 TCP 连接,探测端口是否 open / closed / filtered
最终我改回了 Java 实现。并开源在了 https://github.com/hellodk34/LanPortScanner
直接下载 jar 包:https://github.com/hellodk34/LanPortScanner/releases
一个用 Java 编写的局域网端口扫描工具,支持 TCP 和 HTTP 协议扫描。现成的 LanPortScanner.jar 文件(仅5.18KB)请在项目 release 页面下载。
# Windows一键操作(双击运行)
build_win.bat
# linux/macOS一键操作
chmod +x build_linux.sh
./build_linux.sh
# 或者手动编译
javac -encoding UTF-8 LanPortScanner.java
jar cfe LanPortScanner.jar LanPortScanner LanPortScanner.class
# TCP 扫描(默认2秒超时)
java -jar target/LanPortScanner.jar tcp 192.168.1.1 192.168.1.254 80
# HTTP 扫描(自定义超时3秒)
java -jar target/LanPortScanner.jar http 192.168.10.1 192.168.10.254 8080 3
# 跨网段扫描
java -jar target/LanPortScanner.jar tcp 192.168.0.1 192.168.1.254 22 5
java -jar LanPortScanner.jar <协议> <起始IP> <结束IP> <端口> [超时秒数]
| 参数 | 必需 | 说明 | 示例 |
|---|---|---|---|
| 协议 | ✅ |
tcp 或 http(不区分大小写) |
tcp |
| 起始IP | ✅ | 起始IP地址(必须是局域网IP) | 192.168.1.1 |
| 结束IP | ✅ | 结束IP地址(必须是局域网IP) | 192.168.1.254 |
| 端口 | ✅ | 目标端口号(1-65535) | 80 |
| 超时时间 | ❌ | 超时秒数(默认2秒) | 3 |
========================================
🔍 局域网端口扫描器
========================================
协议类型: TCP
IP范围: 192.168.1.1 - 192.168.1.254
目标端口: 22
超时设置: 2 秒
扫描总数: 254 个IP
========================================
开始扫描...
✅ 发现设备: 192.168.1.100:22
✅ 发现设备: 192.168.1.150:22
========================================
🏁 扫描完成
========================================
总耗时: 15.23 秒
扫描IP数: 254
发现设备: 2 个
发现的设备列表:
- 192.168.1.100:22
- 192.168.1.150:22
========================================
本项目为纯Java实现,无外部依赖,可直接编译运行。

2026-02-01 19:08:58
这里我们把整个 OpenWrt 系统迁移到 ext4 磁盘上,通过 extroot 机制实现一个更大的根分区。
在 OpenWrt 中,extroot 是一种利用外部存储设备(USB / SATA / SD 卡)来扩展系统可写空间的机制。
它并不是对原有根分区进行在线扩容,而是通过启动后切换根文件系统或 overlay,让系统运行在一个容量更大的外部磁盘之上。
启动阶段
squashfs,配合一个很小的可写 overlay
挂载外部存储
block-mount 根据 /etc/config/fstab
ext4)根文件系统切换
/overlay(或直接接管 /)从系统视角看:
/ 或 /overlay 位于外部磁盘ext4
对上层应用和用户来说:
extroot 并不是只挂载一个空目录作为 overlay,
而是要求外部存储上存在完整、可启动的 OpenWrt 根文件系统结构。
因此在启用 extroot 前,通常需要将当前系统的 / 完整复制到外部磁盘中,
以确保 init、库文件和配置在切换后仍然可用。
一句话总结:
extroot 通过在启动过程中将 OpenWrt 的可写根文件系统迁移到外部存储设备,实现“逻辑上的根分区扩容”。
好的,官话结束,我们来讲怎么做。很简单,建议安装 diskman luci 界面配合命令行操作。
一、安装 diskman
# 更新一下软件源
# opkg update
# 安装 diskman 程序和中文汉化包
# opkg install luci-app-diskman luci-i18n-diskman-zh-cn
二、新建分区并格式化(作为新的根使用,以此扩展根分区大小)
在 luci 界面中切换到“DiskMan 磁盘管理”,编辑当前磁盘,在“分区信息”最后一行直接新建分区,这样可以快捷创建分区,不需要手动计算“起始扇区”和“中止扇区”,新建好之后建议格式化此分区为 ext4 文件系统。


三、回到挂载点将上述分区挂载为根/
如下图

在“保存并应用”之前,执行如下命令(就是页面上显示的那几行),我添加了注释,更方便理解每一行干了啥,作用是什么
# 创建临时挂载点,用于访问当前正在运行的内部根文件系统
mkdir -p /tmp/introot
# 创建临时挂载点,用于挂载即将作为 extroot 的外部磁盘分区
mkdir -p /tmp/extroot
# 将当前根目录 / 绑定挂载到 /tmp/introot,便于完整复制现有系统
mount --bind / /tmp/introot
# 挂载外部磁盘分区(ext4),作为未来的 extroot 根文件系统
mount /dev/sda1 /tmp/extroot
# 使用 tar 管道方式,将整个根文件系统原样复制到外部磁盘分区
tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -
# 卸载临时挂载的内部根文件系统
umount /tmp/introot
# 卸载外部磁盘分区,确保数据完整写入磁盘
umount /tmp/extroot
逐行执行之后,再“保存并应用”。
现在通过 df -hT 查看根分区大小是 290.4MB

四、重启生效
重启之后查看根分区大小,变成了磁盘余下空间 28.4G,成功实现根分区扩容。

2026-01-23 14:53:00
最近我使用的机场节点都更新了,我在一个新的 immortalwrt 环境上使用原有的订阅链接,始终无法订阅到节点,我还一直以为是我找的订阅转换服务有问题。试了好几个服务,都无法订阅到节点。
测试了这些订阅转换服务(subscription converter)
订阅全部都失败了。passwall 给出的错误要么是“订阅失败,可能是订阅地址无效,或是网络问题,请诊断”,要么是 “成功解析【xxx_airport】节点数量: 0”。
我开始了研究。
旧 immortalwrt 环境上的 passwall 版本
# opkg list-installed | grep passwall
luci-app-passwall - 25.9.23-r1
luci-i18n-passwall-zh-cn - 25.270.37028~65fa739
新 immortalwrt 环境上的 passwall 版本
# opkg list-installed | grep passwall
luci-app-passwall - 25.12.16-r1
luci-i18n-passwall-zh-cn - 25.350.06350~ba7f272
版本确实不一样,源里的版本升级了。但因为 op 源里一般只保存最新的软件包,我放弃了降级,也放弃了卸载和重新安装同样的版本(要去找同版本 ipk 文件),后面的经历证明此举明智。
我开始对订阅链接产生兴趣,看看能发现什么规律。
果然!发现规律了,我使用的机场提供的订阅链接,我一般只使用 ?clash=1 的那个,在 passwall 中订阅时需要先转换,把节点目标设成 shadowsocks(SIP002)
注:SIP002 是 Shadowsocks 官方定义的标准 URI 格式规范(即标准的“ss://”链接格式)
我把 clash=1 的订阅拉到本地发现就是 clash 的 yaml 文本,最终发现所有节点都是 type: ssr,原来是这样!!!就是因为 clash 的订阅链接只返回了 ssr 节点,但我本地的 passwall 使用 ss/trojan/vmess/vless/hysteria2 节点,因为没有安装 ssr。

我又从机场的web后台复制了一个 ss 节点的 ss:// 链接手动导入,可以成功导入!遗憾的是,机场提供了批量复制 ssr 链接功能,但是没有提供批量复制 ss 链接功能。于是我开始想办法手动转换。
我本地访问了 quanx 的订阅链接 ?list=quantumultx,得到一个94行的文本,乍一看就能看出规律,47个节点,每个节点各有一个 ss 链接和一个 ssr 链接。长得像这样
shadowsocks=xxx.com:3044, method=chacha20-ietf, password=xxx, ssr-protocol=auth_aes128_sha1, ssr-protocol-param=xxx, obfs=plain, obfs-host="xxx, tag=Standard|台湾|IEPL|01
shadowsocks=xxx.com:30333, method=rc4-md5, password=xxx, obfs=http, obfs-host=xxx, obfs-uri=/, tag=Standard|台湾|IEPL|01
于是我将他们转到 linux 下,
cat > sub.txt<<'EOF'
94 行文本
EOF
执行命令 grep -v 'ssr-protocol=' sub.txt > sub_new.txt,将 ssr 节点对应的行全都删掉,重定向到新文件,新文件里全部都是 ss 节点信息。
最后我就是找规律,看机场给的 ss 链接是根据什么规律生成的,然后将剩下的 47 个节点数据全部转换成 ss:// 文本链接。附上我使用的 java 代码(java17)
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class SSUrlParser {
public static String parseAndGenerate(String line) throws Exception {
Map<String, String> map = new HashMap<>();
for (String part : line.split(",")) {
String[] kv = part.trim().split("=", 2);
map.put(kv[0], kv[1]);
}
String[] hostPort = map.get("shadowsocks").split(":");
String host = hostPort[0];
int port = Integer.parseInt(hostPort[1]);
String userInfo = map.get("method") + ":" + map.get("password");
String userInfoBase64 = Base64.getEncoder().encodeToString(userInfo.getBytes(StandardCharsets.UTF_8));
// 插件参数
String pluginRaw = "obfs-local;obfs=" + map.get("obfs") + ";obfs-host=" + map.get("obfs-host");
String pluginEncoded = URLEncoder.encode(pluginRaw, StandardCharsets.UTF_8.toString());
// group 参数:Base64 编码
String group = map.getOrDefault("tag", ""); // 没有单独 group 时用 tag
// String groupBase64 = Base64.getEncoder().encodeToString(group.getBytes(StandardCharsets.UTF_8));
String groupName = "机场名称特有字符串"; // 这里的规律,可能不同机场不一样
String groupBase64 = Base64.getEncoder()
.withoutPadding()
.encodeToString(groupName.getBytes(StandardCharsets.UTF_8));
// tag 参数 URL encode
String tagEncoded = URLEncoder.encode(group, StandardCharsets.UTF_8.toString());
return "ss://" + userInfoBase64
+ "@" + host + ":" + port
+ "/?plugin=" + pluginEncoded
+ "&group=" + groupBase64
+ "#" + tagEncoded;
}
public static void main(String[] args) throws Exception {
String input = """
shadowsocks=xxx.com:30333, method=rc4-md5, password=xxx, obfs=http, obfs-host=xxx, obfs-uri=/, tag=Standard|台湾|IEPL|01
shadowsocks=xxx.com:30334, method=rc4-md5, password=xxx, obfs=http, obfs-host=xxx, obfs-uri=/, tag=Standard|台湾|IEPL|02
shadowsocks=xxx.com:30335, method=rc4-md5, password=xxx, obfs=http, obfs-host=xxx, obfs-uri=/, tag=Standard|台湾|IEPL|03
"""; // 使用 Text Blocks 特性
// 遍历每一行
input.lines()
.map(String::trim) // 去掉首尾空白
.filter(line -> !line.isEmpty()) // 判空
.forEach(line -> {
try {
System.out.println(parseAndGenerate(line));
}
catch (Exception e) {
throw new RuntimeException("当前行计算出了问题...");
}
}); // 调用自定义方法
}
}
上述代码执行后即可将机场ss节点信息全都转换成 ss链接,但是注意,这可能不适用于你的机场。最终我将47行ss链接成功一次性导入 passwall:节点列表标签页下 -> 通过链接添加节点功能 -> 一行一个,全部复制到这里,可以添加一个单独的分组。

没有使用订阅的方式(机场没有提供符合我需求的订阅链接),成功批量导入节点!完美。

不过后来我发现,将 ?list=quantumultx 这个订阅链接作为源,然后转换为 ShadowsocksSIP002,最终生成临时订阅链接放进 passwall,成功订阅 😂️,解析到了93个,应该是把所有的节点 ss/ssr 都识别出来了,passwall 这边每个节点显示了两次,至于为什么还少一个(应该94个),就不再探究了。
最终学到了:ss:// URL 中,只有 method / password / host / port / plugin 是“协议级可推导”的group 和 tag 都是“订阅生成器的业务字段”
还有
String groupBase64 = Base64.getEncoder()
.withoutPadding()
.encodeToString(groupName.getBytes(StandardCharsets.UTF_8));
和
String groupBase64 = Base64.getEncoder()
.encodeToString(groupName.getBytes(StandardCharsets.UTF_8));
的区别。
2025-09-13 23:41:00
本文记录给我的机械革命钛钽PLUS换屏。
旧屏幕用了将近4年(2021年6月购入),今年花屏(几月份来着,不记得了,这不重要),然后全黑,最后怎么都不亮了。我外接了两块显示器使用,一根使用 typec to hdmi 接入显示器,另一根使用 hdmi to hdmi 接入显示器。最近准备给这个电脑换一下屏幕,了解到机革同年甚至22年几个系列都喜欢用京东方 NY2 屏幕,网上能搜到很多翻车信息。
哔哩哔哩这两个视频评论区有很多有价值的信息,如果你有同款电脑并且屏幕有问题的不妨看看。
机械革命钛钽PLUS 花屏,黑屏,坏屏问题:
【铭达数码电子商行:机械革命蛟龙7 钛钽Plus 旷世 笔记本屏幕换屏教程。】 https://www.bilibili.com/video/BV1PN4y1W7jf/
【机械革命 钛钽plus 刚过保 NE173QHM-NY2屏幕挂了 自己换屏】 https://www.bilibili.com/video/BV1mP4119768/
原屏幕
我在淘宝买了一块新的,A+品质,将近无暇的屏幕,我选择了NZ2这个型号
屏幕到货,准备拆下旧的屏幕,建议参考上面第二个bilibili视频,记住要点
我拆下了的旧的显示屏
一次就成功点亮显示屏
旧的NY2屏幕
新的NZ2屏幕
换上了新的 NZ2 显示屏,但是开机后只能 60Hz 使用,切换到 240Hz 就闪屏。我在网上查询,都说是intel核显电压不足导致的,也可能就是核显问题,和供电电压无关。再问客服,客服给了我一个视频,让我从 bios 里切换到 dGPU only (也就是独显 only),默认是 hybrid 核显独显混合模式。实际上就是机械革命的电竞控制台独显直连的开关,我观察了开和关会让bios里变成混合模式/dGPU only,只不过有些人说在bios里修改会更稳定 那就切独显直连吧
但我切换到独显就无法开机,重启(或者开机)进入到输入密码的界面机器就强制关机了(键盘灯灭了),后面问客服,客服找到了技术,技术找我要电话,打电话给我,但他也并没有给我任何实质性的建议,或者操作方式,于是我就说算了吧,我就60Hz 用。。。
我在网上搜了一些资料
我都测试了,但没有成功。在我抱着试试的态度,将 Secure Boot 关掉,最后又因为 BitLocker 问题重新打开了 Secure Boot,最终保存 bios 设置重启电脑,居然就成功启动了!!!
最后核显被成功屏蔽,资源管理器当中只显示了 GPU0 - nvidia rtx 3060 laptop gpu,并且在显示设置中将刷新率调到 240Hz 也正常运行,不会闪烁了,不会闪屏了,太棒了!

并且这一个 display 也显示 connected to rtx 3060

所有测试完毕,使用3mm黑色双面胶粘贴在屏幕周围塑料边框上再将边框贴紧
将屏幕装好,再拆下D壳,将电池排线重新连接上,最后装好D壳,就完成了,完美搞定(实际上不是完美,在用纤维布擦拭屏幕时将呼吸灯的一颗灯珠碰掉了。。😭️ 不过也无伤大雅就是)
2025-09-05 17:13:00
我去年8月出去旅行,今年8月出去旅行,在外面访问家里 memos,都挂了,我每次都吐槽出远门家中网络必挂,-_-。一开始我一直认为是主路由网络挂了,以前我用的是野路子来的 OpenWrt 固件(比如各种高大全固件),但是今年我转战了 ImmortalWrt 官方构建的固件,这个稳定性很好建议使用这个。建议从他们的 firmware-selector 网站下载适合你的镜像 -> https://firmware-selector.immortalwrt.org/
最近发现可能不是这么回事,应该是我的 pve 宿主机和上面的 debian11 虚拟机的问题,这个问题从一次远程访问家中 pve 宿主机执行一些命令时候报错 Input/output error 开始。
我的 pve 宿主机上有两块磁盘,如下
NAME SIZE MODEL
sda 3.6T ST4000VX000-2AG166
nvme0n1 238.5G HS-SSD-C2000Pro 256G
在宿主机上执行各种需要读写磁盘的命令都会报错 Input/output error
回家后发现主机温度很高,直接强制断电了,然后将外壳拆下,发现机械硬盘非常烫,我就赶紧拿起风扇对着吹。
大概吹了30多分钟,主机很凉快了,尝试再次开机,发现一切如常。支线:决定购买风扇增加散热
第三天发现memos又无法访问了,进 pve 终端查看虚机状态是 RUNNING,但是 ping 不通,最后只好执行 qm stop 100 && qm start 100 重启了一下虚机,也正常了。接着进入debian11 的终端立即检查,发现了关键错误 Read-only file system, rsyslogd: file '/var/log/syslog' write error - OS error: Read-only file system
从日志中的 Read-only file system 错误可以看出,你的系统根文件系统已被内核强制挂载为 只读模式 。这是Linux系统检测到严重磁盘错误时的保护机制,防止进一步数据损坏。以下是详细分析和解决方案:
关键日志解读
核心错误 :rsyslogd: file '/var/log/syslog' write error - OS error: Read-only file system
含义 :rsyslog(系统日志服务)尝试写入 /var/log/syslog 时失败,因为文件系统已处于只读状态。
还有一段核心错误
kernel: FS-Cache: Duplicate cookie detected
kernel: FS-Cache: O-cookie c=00000000439c879b [p=000000000e48f285 fl=222 nc=0 na=1]
kernel: FS-Cache: N-key=[16] '0400000002000000020008010a0a0a01'
这段日志帮我找到了磁盘挂载的问题。我查看 /etc/fstab 发现了4个无效挂载点(下图是我注释之后截的)
原来是我以前在软路由(10.10.10.1)上接了这4块硬盘,但是后来取下了这几块硬盘,然后这边没删除,给忘掉了。几年前就意识到要将家里各种设备的职责划分清楚,软路由就只专心做好路由,不搞下载和做种,所以当时就把这几块硬盘拔掉了,对应的 NFS 服务也就不存在了。所以现在这个小问题的解决办法就是删除这几行或者注释这几行。
pve宿主机 /etc/fstab 这个文件还硬编码写死了这几个挂载,导致拖慢系统,有时候甚至卡死了,于是我赶紧添加了注释,重启了pve,再次执行 journalctl -b -p 3 --no-pager 输出如下
-- Journal begins at Sun 2025-07-27 21:59:23 CST, ends at Fri 2025-09-05 22:04:09 CST. --
-- No entries --
没有输出就是最好的状态,于是我以为这就解决问题了。
第四天发现又访问不了 memos 了。这一次一定要找到原因。在我大量的 google, bing, chatgpt, deepseek 之后,看到一些有用的信息,猜测有可能是内核版本 5.13.19,pve版本7.1-7,还有我的CPU Intel(R) Xeon(R) CPU E3-1265L v3 @ 2.50GHz 之间的冲突。也看到了国内恩山论坛这个帖子
https://www.right.com.cn/forum/thread-8266555-1-1.html [虚拟机相关] 记录对于说N5105在PVE不稳定无故死机的一种解决方法
最后我增加了 non-free 的四个源之后,试图安装 intel-microcode,期待解决问题。
新的源文件/etc/apt/sources.list内容
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换
deb https://security.debian.org/debian-security bullseye-security main contrib
deb https://security.debian.org/debian-security bullseye-security main contrib non-free
执行 apt update 之后执行 apt-get install intel-microcode,成功安装 intel-microcode 软件包

一段和chatgpt的对话,它照着日志讲出了核心原因
目前接着观望pve宿主机和debian11虚机的运行情况。
2025年09月15日上午更新,稳定运行,10.10.10.5 是虚拟机 IP,宿主机本身的IP是 10.10.10.3,虚机没断过网络,各种服务也没有 down 过。基本上可以判断出之前就是因为 intel 微码没有安装导致的虚拟机 down…… 不过这个机器组装以及系统安装是在 2021 年,怎么现在才出现这样的问题呢……有点不解
# uptime
08:54:49 up 8 days, 19:06, 1 user, load average: 0.02, 0.03, 0.05
# ping -c 4 10.10.10.5
PING 10.10.10.5 (10.10.10.5) 56(84) bytes of data.
64 bytes from 10.10.10.5: icmp_seq=1 ttl=64 time=0.170 ms
64 bytes from 10.10.10.5: icmp_seq=2 ttl=64 time=0.198 ms
64 bytes from 10.10.10.5: icmp_seq=3 ttl=64 time=0.215 ms
64 bytes from 10.10.10.5: icmp_seq=4 ttl=64 time=0.214 ms
--- 10.10.10.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3074ms
rtt min/avg/max/mdev = 0.170/0.199/0.215/0.018 ms
2025-08-19 11:13:00
最近发布了徒步反穿武功山,以及自驾皖浙赣的游记文章,点我直达 ,我分享到了 v2ex 以及 linux.do ,各路网友都说图床太慢了。由于原文图片数量的确较多,我的图床部署在家里,然后只有 VPS 的6Mbps小水管,在并发不大的时候阅读文章还行(图片加载的速度还OK,并且有配置懒加载,所以体验还行),但是昨天发了帖子之后有几波流量激增。于是我寻找办法:
最终我买了一个月的 edgeone,只花费4.7元~
现在部署好了 cdn,访问速度大大增加。当前时间点 2025-08-19 10:30:42 edgeone 流量情况
我这篇文章不算是教程吧,只能是个人的心路历程,我也不想写保姆级教程,太费事了。在此就简单讲讲过程和原理。
940304.xyz 然后经过认证我顺利的部署成功,也看到 edgeone 流量页面产生了非常多的数据,当前我博客中的多图文章访问体验肯定就好很多了。
但是后面我发现一个问题,就是图片资源访问后返回头当中的 Cache-Control 始终是 max-age=3600,这说明 cdn 边缘节点只会缓存这张图片一个小时,如果用户在一个小时之后访问同链接会无法击中缓存,导致cdn节点回源获取原始文件,这势必会增加很多源站的流量,我的公网VPS以及家里服务器的流量都会增大,设置CDN的效果就大为下降了,而且我这就是图床程序,资源是静态的,不需要长时间变动。一般情况下图片资源都会设置30天后过期,也就是 max-age=2592000(单位s),于是我开启了排查。
在部署 edgeone 之前,我的图床架构是这样的
在部署 edgeone 之后,我的图床架构变成这样
在任意有网络机器执行 curl -I https://image.940304.xyz/i/2025/05/21/682dd7d3137c3.jpg 发现返回 Cache-Control: max-age=3600,我开启了漫长的排查,总结下来就是
1、在VPS上修改 nginx 配置,显式增加 Cache-Control 的返回头,设置成 public, max-age=2592000,但是无果
2、在以上基础上,区分单独的匹配图片的 location,因为之前是一个反向代理 location / 就能搞定
配置是这样的
location ~* \.(jpg|jpeg|png|gif|webp|ico|css|js|svg|woff2)$ {
proxy_pass http://127.0.0.1:81;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
# 30天缓存(仅对静态资源)
add_header Cache-Control "public, max-age=2592000, s-maxage=2592000";
# 跨域允许设置,允许所有跨域
add_header 'Access-Control-Allow-Origin' *;
# 自定义 Debug 头部(仅用于测试)
add_header HDKRESP "This-request-matched-jpg-location_test"; # 新增
# 跨域允许设置结束
# 防盗链设置,因为上述跨域是允许所有的,这里就要设置防盗链从而进行域名的白名单设置
valid_referers none blocked 940304.xyz *.940304.xyz hellodk.cn *.hellodk.cn hellodk.com *.hellodk.com 10.10.10.5;
#nginx不允许嵌套if语句,这个很关键,否则nginx -t 会测试失败,无法reload或者start nginx服务
set $block_access 0;
# 如果 referer 无效
if ($invalid_referer) {
set $block_access 1;
}
# 根据之前的判断结果决定是否拦截
if ($block_access) {
rewrite ^/ https://i.imgur.com/CB4Fseq.jpeg;
}
}
这样的确能增加Cache-Control 的返回头,但是edgeone 那边始终没有生效。我还去lsky pro 图床程序的源 apache2 服务端添加了 Cache-Control 头,因为根据 edgeone 的规则,会先读源站的缓存相关的返回头(Cache-Control:s-maxage, Cache-Control:max-age, Expires),如果有会优先遵循源站的配置
edgeone 的文档:https://cloud.tencent.com/document/product/1552/87651
edgeone默认缓存规则
默认缓存规则文字说明
3、发现修改vps nginx配置无果,我进入了 lsky pro 容器,做了以下操作
# Apache 需要启用 mod_headers 才能使用 Header set 指令,启用这个模块
a2enmod headers
# 修改生效配置文件 `/etc/apache2/sites-enabled/000-default.conf` 可以通过命令 `apache2ctl -S` 寻找生效的虚拟主机所在文件
# 并增加如下配置
<LocationMatch "^/i/.*\.(jpg|jpeg|png|gif|webp|ico|bmp)$">
Header set Cache-Control "public, max-age=2592000"
</LocationMatch>
# 重启apache2服务
service apache2 restart
现在图床源站点会给静态图片资源返回 Cache-Control 了,在图床容器中执行 curl -I http://localhost/i/2025/05/21/682dd7d3137c3.jpg 有如下返回
HTTP/1.1 200 OK
Date: Tue, 19 Aug 2025 03:11:18 GMT
Server: Apache/2.4.53 (Debian)
Last-Modified: Wed, 21 May 2025 13:40:35 GMT
ETag: "ef67-635a58279843c"
Accept-Ranges: bytes
Content-Length: 61287
Cache-Control: public, max-age=2592000
Content-Type: image/jpeg
在 edgeone 管理页面清除了这个URL https://image.940304.xyz/i/2025/05/21/682dd7d3137c3.jpg 的缓存,从 EO-Cache-Status MISS 到 HIT(也就是第一次请求未击中缓存cdn节点回源了,到第二次击中了缓存),返回的 max-age 仍然是 3600
4、开始排查 edgeone 这边,终于找到了原因所在!
需要在这里点击站点加速,我以为默认的全局配置就是对的,因为设置的是遵循源站的 Cache-Control,所以一直没细看
这里全局配置是正确的,没毛病,但是右侧还有一个规则引擎默认是打开的(但是默认没有任何配置,尼玛坑死我),并且优先级更高。
规则引擎默认打开并且默认无配置坑我导致max-age一直是3600!!!(欲哭无泪
最后我因为不需要个性的规则引擎配置,我就将这边关掉了,直接使用全局配置,遵循源站的 Cache-Control 即可。
最终执行 curl -I https://image.940304.xyz/i/2025/05/21/682dd7d3137c3.jpg 返回了 Cache-Control: public, max-age=2592000 最终解决,这样可以减少 cdn 回源的频率,需求满足!
ok,如果你有任何疑问,欢迎留言与我讨论。
edgeone国际版测速分享到X或者Facebook得FreePlan,网站 https://edgeone.ai 甚至分享到X+分享到Facebook可得两份FreePlan
我等当前买的国内版失效之前的几天就切换到国际版。但是需要注意的是,腾讯云的一份身份认证只能实名制一个腾讯云账号,edgeone国际版要想使用中国大陆节点需要实名认证的账号才行。我的身份已经实名了国内版的了,后面大概就选择国际节点
国内版edgeone产品,4.7元买的一个月