可行但需关闭EnableSendfile和EnableMMAP、严格绑定MIME类型、脚本须用sys.stdin.buffer.read()处理二进制流、设timeout=5防卡死,否则易空白响应或worker崩溃。

直接用 mod_ext_filter 调用外部 Lua 或 Python 脚本处理 Apache 响应流是可行的,但必须绕过默认机制陷阱、严格遵循二进制流契约,否则极易出现空白响应、乱码、超时或 worker 卡死。
必须关闭内核加速机制
Apache 对静态文件(如 .jpg、.html)默认启用 EnableSendfile 和 EnableMMAP,导致响应体不经过用户态过滤器,直接由内核发送。你的脚本根本不会被执行。
- 在虚拟主机或全局配置中显式关闭:
EnableSendfile OffEnableMMAP Off - 该设置必须放在
<VirtualHost>或服务器级上下文中,不能只写在ExtFilterDefine里 - 重启 Apache 后,用
curl -I /test.jpg检查响应头是否不再含X-Sendfile或Accept-Ranges: bytes(间接表明 sendfile 已退化)
正确绑定 MIME 类型与过滤器
mod_ext_filter 不按文件扩展名匹配,而是依据响应头中的 Content-Type。如果类型不匹配,过滤器不会触发。
- 用
AddType image/jpeg .jpg确保 .jpg 文件被识别为image/jpeg,而非image/jpg(常见误配) - 定义过滤器时明确指定
intype和outtype:ExtFilterDefine watermark mode=output intype=image/jpeg outtype=image/jpeg cmd="/path/to/watermark.py" - 若需处理多种类型,可重复定义多个 filter,或用
FilterProvider+mod_filter做条件路由(更灵活但复杂度上升)
外部脚本必须满足二进制流契约
Apache 将原始响应体字节流通过 stdin 推入脚本,要求脚本读完全部输入、无额外输出、原样或修改后写入 stdout。任何偏差都会中断 HTTP 流。
立即学习“Python免费学习笔记(深入)”;
- Python 示例关键点:
• 开头加#!/usr/bin/env python3 -u(-u强制无缓冲)
• 用sys.stdin.buffer.read()读取原始字节,禁用sys.stdin.read()
• 处理失败时(如 PIL 打不开损坏图),必须 fallback 输出原始输入字节,不可抛异常或静默退出
• 避免print()、logging、echo等向stdout写非响应内容的行为 - 调试建议:
• 先脱离 Apache 测试:cat test.jpg | ./watermark.py > out.jpg,再比对stat -c "%s" test.jpg out.jpg确认长度一致
• 在脚本开头加sys.stderr.write("filter started\n"),配合LogLevel debug查看是否执行
必须设超时并避免高并发阻塞
mod_ext_filter 是同步阻塞调用,每个请求 fork 一个新进程。图像处理类脚本(尤其用 PIL)在普通服务器上单次耗时常达 300–800ms,10 并发即可拖垮 Apache。
- 强制添加超时保护:
ExtFilterOptions timeout=5(单位秒),防止单张图 hang 住整个 worker - 生产环境不建议直接用 Python/PIL 做实时水印:
• 改用 C 编写的轻量工具(如基于 libgd 的二进制)
• 或前置到 Nginx + Lua(body_filter_by_lua*)做异步流处理
• 或交由 CDN 层统一水印,Apache 仅作低频兜底 - 若坚持使用,务必限制并发连接数(
MaxRequestWorkers)并监控scoreboard中W(sending reply)状态占比


















