宝塔面板升级 13.0.0 后 Nginx 免费防火墙全是 undefined?替换两个文件就好
最近估计不少用宝塔面板的小伙伴,升级到 13.0.0 之后,发现「Nginx 免费防火墙 8.3」突然故障了。表现为:无法查看网站日志,无法查看封锁历史,webshell 查杀结果显示异常。
省流:直接点「这里」看如何修复
升级宝塔面板 13.0.0 后,Nginx 免费防火墙为什么全是 undefined

基本 Nginx 免费防火墙面板里所有涉及日志显示部分的功能都会是类似上图的效果,一堆 undefined,涉及对应项的按钮一点就报错。
其实造成这个 Bug 的原因非常简单:Nginx 免费防火墙 8.3 是基于 Python 3.8 写的,宝塔面板升级到 13.0.0 后,系统运行环境从 Python 3.8 升级到了 Python 3.13。而 Python 3.13 完全移除了整个 cgi 标准库,导致了 Nginx 免费防火墙 8.3 的日志解析崩溃和日志格式越界报错。于是我就拉着 Gemini 3.7 Flash 一起把这个 Bug 修了一下,把整个防火墙都改成了 Python 3.13 兼容版。
宝塔 Nginx 免费防火墙 8.3 修复补丁:下载与安装
- 补丁下载地址:蓝奏云
- 安装步骤:
- 先备份
/www/server/panel/plugin/free_waf路径下的free_waf_main.py与fail2ban_main.py文件。 - 用压缩包里的
free_waf_main.py与fail2ban_main.py,替换掉/www/server/panel/plugin/free_waf路径下的同名文件。 - 刷新宝塔面板的 Nginx 免费防火墙页面,确认日志不再显示
undefined,封锁历史和 webshell 查杀结果恢复正常。
- 先备份
搞定,收工,Nginx 免费防火墙 8.3 已经可以在 Python 3.13 环境下正常工作了。

修复补丁到底修了什么东西
这些全是代码,如果不关心到底怎么改,只想修复问题的,可以关闭浏览器标签页了(当然要是能留言给个好评就更好啦)
1. 替换 Python 3.13 已移除的 cgi 模块(free_waf_main)
- 原始代码:
import cgi text2 = cgi.escape(str_convert, quote=True) tmp_data = json.loads(cgi.escape(line)) - 补丁代码:
import html text2 = html.escape(str_convert, quote=True) tmp_data = json.loads(html.escape(line, quote=False)) - 修了什么:
Python 3.13 彻底移除了整个
cgi标准库,原版代码依赖cgi解析日志数据,所以在系统环境升级到 Python 3.13 后就开始大量报错。(这就是最开始截图中故障现象的成因)补丁给它改成用标准库html.escape了,并在解析 JSON 数据前指定quote=False,避免双引号被转义破坏 JSON 数据结构。
2. 修复二进制读取日志末尾换行符判断失效(free_waf_main)
-
原始代码:
fp = open(path, 'rb') buf = "" try: fp.seek(-1, 2) except: return [] if fp.read(1) == "\n": fp.seek(-1, 2) - 补丁代码:
fp = open(path, 'rb') buf = "" try: fp.seek(-1, 2) except: fp.close() return [] if fp.read(1) == b"\n": fp.seek(-1, 2) - 修了什么:作者一开始就写错了,在 Python 3 中以二进制模式(
'rb')读取返回的是bytes字节类型,所以b'\n' == "\n"永远都是False,导致程序在倒序解析日志时,对末尾换行的判断是无效的,会导致日志出现一个莫名其妙的空行。补丁改为b"\n"。
3. 规范正则表达式 Raw 字符串前缀(fail2ban_main & free_waf_main)
-
原始代码:
# fail2ban 与 free_waf 中都包含大量类似这样的反斜杠转义的正则表达式字符串 rep_ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}($|[\/\d]+$)" rep = "\nPort\s+(\d+)" if re.search("\d+.\d+.\d+.\d+-\d+.\d+.\d+.\d+$", i): - 补丁代码:
rep_ip = r"^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}($|[\/\d]+$)" rep = r"\nPort\s+(\d+)" if re.search(r"^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$", i): - 修了什么:其实原来我也喜欢他这样写,直到 Python 3.12 开始对无效转义序列(如
\d、\s)抛出SyntaxWarning: invalid escape sequence警告,我才不得不乖乖用正规写法。什么意思?就是\d、\s、\w是正则表达式的语法,不是 Python 字符串的语法。对 Python 语法而言,这些都是非法的转义字符,只不过之前 Python 不会直接报错,而是进入容错回退机制,原样保留反斜杠和后面的字符继续传递,所以之前不会显式报错。补丁统一增加了r""前缀,告知解析器(后面的正则)是个 Raw 字符串。
4. 修复 SSH 默认端口解析的空指针崩溃(fail2ban_main)
-
原始代码:
def _check_ssh_port(self): rep = "\nPort\s+(\d+)" c_file = "/etc/ssh/sshd_config" c = public.readFile(c_file) if not c: return False result = re.search(rep, c) if not c: return "22" return result.group(1) - 补丁代码:
def _check_ssh_port(self): rep = r"\nPort\s+(\d+)" c_file = "/etc/ssh/sshd_config" c = public.readFile(c_file) if not c: return "22" result = re.search(rep, c) if not result: return "22" return result.group(1) - 修了什么:如果 SSH 配置文件未显式配置自定义端口(即
Port字段被注释掉了,SSH 按默认规则使用 22),那result就为None,这时候调用result.group(1)会直接触发报错,导致模块崩溃。
5. 去除对第三方库 IPy 的依赖(free_waf_main)
-
原始代码:
from IPy import IP ip2 = IP("{}/{}".format('.'.join(ip), ip_ddd)) return self.is_ip_zhuanhuang(str(ip2[0]), str(ip2[-1])) - 补丁代码:
import ipaddress net = ipaddress.ip_network(ip, strict=False) return self.is_ip_zhuanhuang(str(net.network_address), str(net.broadcast_address)) -
修了什么:移除对外部第三方模块
IPy的依赖,改用 Python 内置的ipaddress标准库,避免在面板独立虚拟环境升级或重置时因缺失依赖包导致ModuleNotFoundError。
其他次要修改点
- 解禁黑名单类型匹配错误修复:修正
fail2ban_main.unban_ip读取黑名单配置时漏传l=1参数导致空文件被初始化为字典类型、进而引发conf.remove()异常的问题。 - 统一 API 返回方法大小写:将
fail2ban_main._check_get_args中混用的public.ReturnMsg统一纠正为标准的public.returnMsg,避免高版本基础库抛出方法不存在错误。 - 安全性重构:将
fail2ban_main.set_anti中的eval()动态拼接执行重构为更安全的getattr()反射调用。 - 升级构建指令兼容:将
fail2ban_main.update_fail2ban中已在 Python 3.12+ 废弃的python setup.py install升级为python3 -m pip install .。 - 日志返回容错与防白屏:在
free_waf_main.get_safe_logs捕获异常时统一返回空列表[](而非错误字符串),防止前端 JavaScript 遍历报错引发面板页面白屏。 - 文件读写编码与文件描述符保护:将源码扫描与蜘蛛日志的裸读统一改为
with open(..., encoding='utf-8', errors='ignore')上下文管理器,规避编码异常与文件描述符泄漏风险。


网友小宋
2026-08-20 06:20
我是在宝塔论坛看到的解决办法,替换那个py文件就行了
去年夏天
2026-08-20 08:52
论坛就我和另一个人发了补丁,他的补丁只修了第一个问题。我顺手把后面的问题也修了。😆
acevs
2026-08-19 15:57
升级一般大概率遇到问题。技术好的喜欢升级。哈哈~
去年夏天
2026-08-19 16:11
噗哈,有道理,敢于升级,是因为认为自己升级遇到问题时,大概率能搞定。
我是军爸
2026-08-19 12:43
怎么老是有bug要修啊
去年夏天
2026-08-19 14:23
功能越加越多,bug也越修越多。
晓空
2026-08-19 10:27
删库塔还是一如既往的草台班子(乐)
去年夏天
2026-08-19 10:50
这样才能体现你用的是正版宝塔呀😂
obaby
2026-08-19 10:13
升级完还得自己修bug
去年夏天
2026-08-19 10:16
可不是……不过宝塔确实挺神奇的,坚守python3.7那么久,直接一步跨到3.13
obaby
2026-08-19 10:18
10是个分水岭,其实python的版本命名,我也觉得tm离谱。10之后大量的不兼容,干嘛不直接叫4.
去年夏天
2026-08-19 10:50
python的升级确实啊。
3.10 从
collections迁移到collections.abc,我记得当时很多库都在修,这一波少说有折腾小半年3.12 直接把
distutils干掉了,扬了一堆旧版安装脚本。3.13 删了一堆早期的老协议和工具库
cgi,crypt啥啥的,结果就是为了省事,需要装多个版本的python。
花非花
2026-08-19 08:49
好久没登录后台了,都不知道宝塔版本是多少
去年夏天
2026-08-19 09:40
一般不更新也没啥事,毕竟只要不出漏洞,升级=被塞一堆用不上的新功能
花非花
2026-08-19 09:53
这倒是,一般只要功能够用了就很少折腾升级了
Fengc's blog
2026-08-19 06:26
收藏,后续可能会用
去年夏天
2026-08-19 09:36
感觉Nginx免费防火墙的作者需要自己更新一下了,这玩意已经年久失修到到处都有问题,前端看着没事,其实控制台全是报错
热衷于
2026-08-19 00:47
看起来很专业呀, 我的服务器是好心人赞助的拼好机, 估计不会在意升级的, 能用就行
去年夏天
2026-08-19 09:35
他们有些官方插件都没兼容 python3.13 就很草台班子,这么大的版本升级需要慎之又慎啊
Huo
2026-08-19 00:13
很久没有用过宝塔了,之前一直用的宝塔 7.7
去年夏天
2026-08-19 09:09
这不是看他宣传13.0加入了MCP嘛,就升级了一台试试水,结果就遇上这事。
这玩意没问题,确实就别升级的好,越升级越臃肿,东西越多,坏的几率也越高。
依云
2026-08-19 00:08
不建议采用「句柄」的说法——「file descriptor」我们通常译为「文件描述符」。「句柄」是对Windows API中的「handle」的翻译。Linux另有「file handle」的概念。
去年夏天
2026-08-19 09:06
哈哈,接受批评,确实应该叫“文件描述符”,我改一下
Hary
2026-08-18 22:31
新版本至少一个月再升,基本前人把坑都踩了
去年夏天
2026-08-18 22:54
唉,这不是想看看他宣传的的MCP功能嘛,就升了一台的面板,结果就遇到这事。幸好现在有AI,这种问题修起来也快的很。
J.sky
2026-08-18 22:13
厉害,这篇对于使用宝塔的站长还是有帮助的。
去年夏天
2026-08-18 22:53
这个免费防火墙还挺多人用的,宝塔这次升级到3.13太仓促了,一些官方插件都没适配好python3.13。主打一个草台班子