宝塔面板升级 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')上下文管理器,规避编码异常与文件句柄泄漏风险。

