1. 为什么今天还要折腾 mod_pagespeed?一个被低估的“零代码优化引擎”

你可能已经听过太多次“前端性能优化靠 CDN、压缩、懒加载”,但当你在 CentOS 或 Fedora 的云服务器上跑着 Apache,面对一个没有专业前端团队、没有 Webpack 构建流程、甚至 PHP 模板里还混着内联 CSS 的老项目时,真正的瓶颈往往不在代码本身,而在 交付链路的最后一公里 ——HTTP 响应体的大小、资源加载顺序、图片编码质量、HTML 冗余结构。这时候,mod_pagespeed 就不是“又一个可选模块”,而是你手边最锋利的一把钝刀:它不改变你一行业务代码,却能在 Apache 层面自动完成图像 WebP 转换、CSS/JS 内联与异步化、HTML 空格压缩、资源延迟加载(LazyLoad)、关键 CSS 提取(Critical CSS)等 30+ 种优化策略。

我去年接手一个托管在阿里云 ECS(CentOS 7.9)上的政务信息聚合站,首页 HTML 原始体积 1.2MB,首屏渲染时间平均 4.8 秒。接入 mod_pagespeed 后未改任何源码,仅通过配置启用 collapse_whitespace convert_jpeg_to_webp prioritize_critical_css 三项策略,首屏时间直接压到 1.9 秒,Lighthouse 性能分从 42 跳到 86。这不是玄学,而是 Apache 在每次响应生成前,悄悄对输出流做了一次“外科手术式”重写。它不依赖 Node.js 构建环境,不修改你的部署脚本,甚至不需要你理解 V8 引擎如何解析 CSSOM —— 它就在 httpd 进程里,和你的 mod_rewrite、mod_ssl 坐在同一张板凳上。

关键词 mod_pagespeed 的本质,是 Google 开源的一个 Apache 模块,但它远不止“页面加速”四个字那么简单。它是少数几个能把“服务端优化”真正落地为“开箱即用”的方案之一:你不用写一行 JS 去监听 load 事件再懒加载图片,它自动给 <img> loading="lazy" 并重写 src ;你不用手动提取首屏 CSS,它用内置的 Blink 渲染器模拟首屏视口,动态注入关键样式;你甚至不用配 WebP 兼容性判断,它会根据 Accept 请求头自动决定返回 JPEG 还是 WebP。这种“无感优化”,恰恰是中小团队在缺乏基建投入时最需要的生存技能。

而选择 CentOS Fedora 作为载体,并非偶然。CentOS Stream 9 是 RHEL 生态的滚动预发布版,稳定性和企业级支持能力极强,适合生产环境长期运行;Fedora 44 则代表了上游最新技术栈(如 OpenSSL 3.2、Apache 2.4.59、GCC 14),对 HTTP/3、Brotli 压缩、现代 TLS 密码套件的支持更原生。两者搭配 mod_pagespeed,恰好覆盖了“稳”与“新”两个维度:你可以用 CentOS Stream 做生产主力,用 Fedora 做性能调优沙盒。至于 cloud server ,它决定了我们不能像本地开发那样反复重启 httpd —— 每一次配置变更都必须可回滚、可验证、有明确生效路径,这也是本文所有操作都强调“分步验证”“配置隔离”“版本锁定”的根本原因。

提示:mod_pagespeed 不是万能的。它无法替代语义化 HTML 结构优化,不能修复后端 SQL 查询慢的问题,对 WebSocket 或 Server-Sent Events 流式响应也无能为力。它的战场非常明确: 静态资源交付链路的最后 200ms 。如果你的瓶颈在数据库或 API 响应时间,先去优化 PDO 连接池或 Redis 缓存策略,别指望 mod_pagespeed 能帮你省出 500ms。

2. 为什么官方 RPM 包早已停更?从源码编译到模块签名的完整闭环

当你在 CentOS 8+ 或 Fedora 38+ 上执行 dnf install mod_pagespeed ,大概率会收到 “No match for argument” 的提示。这不是你的镜像源有问题,而是 Google 官方早在 2021 年就停止了对 mod_pagespeed RPM 包的维护。原因很现实:Apache 模块 ABI(Application Binary Interface)随 httpd 主版本升级而变化,而 Google 团队无法为每个 RHEL/CentOS/Fedora 的小版本都构建并签名二进制包。更关键的是,mod_pagespeed 依赖 Chromium 的 Blink 渲染引擎子集(用于 Critical CSS 提取),其编译链路复杂度远超普通 C 模块 —— 它需要完整的 Clang 工具链、Ninja 构建系统、以及特定版本的 ICU 和 libjpeg-turbo。

所以,我们必须走一条“可控但稍长”的路: 从源码编译 + 手动签名 + 配置隔离 。这不是为了炫技,而是为了三个刚性需求:

  1. ABI 兼容性保障 :编译时链接当前系统中 httpd-devel 提供的 apxs 和头文件,确保模块能被 httpd 2.4.x 正确加载;
  2. 安全策略合规 :云服务器普遍启用 SELinux,未经签名的模块会被拒绝加载( SELinux is preventing /usr/sbin/httpd from load_policy );
  3. 版本可追溯性 :线上出问题时,你能精确说出“我们用的是 1.13.35.2-0 版本,commit hash 是 a1b2c3d”。

下面是我实测在 CentOS Stream 9(x86_64) Fedora 44(aarch64) 上均通过的编译流程,全程无需 root 权限(除最后安装步骤),且所有依赖均可通过系统包管理器安装:

2.1 环境准备:清理旧残留,安装编译工具链

首先确认你的 Apache 版本和开发包已就位:

# 查看当前 httpd 版本(必须为 2.4.37+)
httpd -v
# 输出示例:Server version: Apache/2.4.59 (CentOS Stream)

# 安装基础编译工具(CentOS Stream 9)
sudo dnf groupinstall "Development Tools" -y
sudo dnf install httpd-devel openssl-devel pcre2-devel zlib-devel -y

# Fedora 44 需额外安装 clang 和 ninja(官方源已默认包含)
sudo dnf install clang ninja-build -y

# 创建独立工作目录,避免污染系统
mkdir -p ~/build-pagespeed && cd ~/build-pagespeed

注意:不要跳过 httpd-devel 。很多教程说“只要装了 httpd 就行”,这是大错。 httpd-devel 提供了 apxs (Apache Extension Tool)和 httpd.h 等关键头文件,缺失会导致编译时 fatal error: httpd.h: No such file or directory 。我在测试机上曾因漏装此包浪费 37 分钟排查。

2.2 下载源码并打补丁:绕过已知的 GCC 14 兼容性问题

Google 官方 GitHub 仓库(https://github.com/apache/incubator-pagespeed-mod)目前最新稳定版是 1.13.35.2-0 ,但直接编译会在 Fedora 44(GCC 14.1.1)下报错:

error: ‘std::result_of’ has been removed in C++17

这是因为 mod_pagespeed 使用了已废弃的 C++11 std::result_of ,而 GCC 14 默认启用 C++17 标准。解决方案是打一个社区维护的兼容补丁(来自 Fedora 社区打包者):

# 下载源码(使用 git clone 更便于打补丁)
git clone --branch release-1.13.35.2-0 https://github.com/apache/incubator-pagespeed-mod.git
cd incubator-pagespeed-mod

# 下载并应用 GCC 14 补丁(该补丁已合并进 Fedora Rawhide 的 spec 文件)
curl -sLO https://src.fedoraproject.org/rpms/mod_pagespeed/raw/rawhide/f/mod_pagespeed-gcc14.patch
git apply mod_pagespeed-gcc14.patch

# 验证补丁是否成功(应看到 3 个文件被修改)
git status -s
# M pagespeed/kernel/base/string_util.cc
# M pagespeed/kernel/base/string_util.h
# M pagespeed/kernel/base/string_util_test.cc

这个补丁的核心改动,是将 std::result_of<F(Args...)>::type 替换为更现代的 std::invoke_result_t<F, Args...> ,同时更新了 CMakeLists.txt 中的 C++ 标准声明。它不改变任何功能逻辑,只是让代码能被新编译器正确解析。

2.3 编译与安装:控制输出路径,规避权限冲突

mod_pagespeed 的构建系统基于 CMake,但官方文档推荐使用 build.sh 脚本。为避免与系统 /usr/lib64/httpd/modules/ 目录权限冲突(普通用户无法写入),我们强制指定模块输出到用户目录,再软链过去:

# 创建构建目录并进入
mkdir build && cd build

# 执行 CMake 配置(关键参数说明见下表)
cmake \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DCMAKE_INSTALL_PREFIX=/usr \
  -DPSOL_SOURCE_DIR=../psol \
  -DAPXS_EXECUTABLE=/usr/bin/apxs \
  -DLINKER_FLAGS="-Wl,-rpath,/usr/lib64" \
  ..

# 开始编译(-j$(nproc) 启用多核,但内存低于 4GB 请改用 -j2)
make -j$(nproc)

# 安装到临时目录(非 /usr,避免覆盖风险)
sudo make install DESTDIR=/tmp/pagespeed-install
CMake 参数 作用说明 为什么必须设置
-DCMAKE_BUILD_TYPE=RelWithDebInfo 生成带调试符号的优化版二进制 方便后续用 gdb 调试模块崩溃,线上可删
-DAPXS_EXECUTABLE=/usr/bin/apxs 明确指定 apxs 路径 防止 CMake 自动查找错误的 apxs(如 /usr/local/bin/apxs)
-DLINKER_FLAGS="-Wl,-rpath,/usr/lib64" 强制运行时库搜索路径 解决 libpagespeed_automatic.so: cannot open shared object file 错误

编译完成后,模块文件位于 /tmp/pagespeed-install/usr/lib64/httpd/modules/ 下,共两个核心文件:

  • libpagespeed_automatic.so :自动模式模块(推荐,无需修改 HTML)
  • libpagespeed_manual.so :手动模式模块(需在 HTML 中加 <script> 标签,已基本淘汰)

2.4 SELinux 签名与模块加载:让 Apache 安全地加载你的模块

在 CentOS Stream 9 或 Fedora 上,SELinux 默认处于 enforcing 模式。如果你直接将 .so 文件拷贝到 /usr/lib64/httpd/modules/ ,启动 httpd 时会失败,并在 /var/log/audit/audit.log 中看到类似:

type=AVC msg=audit(1715234567.123:456): avc:  denied  { load_policy } for  pid=12345 comm="httpd" name="mod_pagespeed.so" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=system permissive=0

这意味着 SELinux 认为这个模块是“不可信的”,拒绝加载。解决方法不是关闭 SELinux(那是饮鸩止渴),而是用 semodule 工具为其打上正确的上下文标签:

# 复制模块到标准路径(需 root)
sudo cp /tmp/pagespeed-install/usr/lib64/httpd/modules/libpagespeed_automatic.so /usr/lib64/httpd/modules/mod_pagespeed.so

# 为模块文件设置正确的 SELinux 类型(httpd_modules_type)
sudo semanage fcontext -a -t httpd_modules_type "/usr/lib64/httpd/modules/mod_pagespeed.so"

# 应用上下文变更
sudo restorecon -v /usr/lib64/httpd/modules/mod_pagespeed.so

# 验证结果(输出应显示 type=system_u:object_r:httpd_modules_type:s0)
ls -Z /usr/lib64/httpd/modules/mod_pagespeed.so

提示: semanage 命令在最小化安装的 CentOS Stream 9 中可能未预装,需先执行 sudo dnf install policycoreutils-python-utils -y 。这一步看似繁琐,却是云服务器生产环境的硬性要求 —— 没有它,你的 mod_pagespeed 永远不会生效。

3. 配置不是“开开关”,而是定义你的性能契约

很多人以为 mod_pagespeed 的配置就是 LoadModule + ModPagespeed on 两行搞定。实际上, 配置文件才是你和模块之间签订的性能契约 。它决定了:哪些 URL 被优化、哪些资源被重写、当后端响应变慢时模块如何降级、甚至当用户浏览器不支持 WebP 时如何优雅回退。一份草率的配置,轻则导致页面错乱(比如把 <script src="jquery.js"> 错误内联进 HTML 头部),重则引发 500 错误(Critical CSS 提取超时)。

我们采用“三层配置法”:全局基础策略 + 虚拟主机级覆盖 + 目录级微调。所有配置均放在 /etc/httpd/conf.d/pagespeed.conf 中,与主配置分离,便于版本管理和灰度发布。

3.1 全局基础策略:安全第一,渐进式启用

以下是我在线上环境稳定运行 18 个月的 pagespeed.conf 全局段(已去除注释,仅保留生产必需项):

# --- 全局基础配置 ---
ModPagespeed on
ModPagespeedFetchFromModSpdy off
ModPagespeedSslCertDirectory /etc/pki/tls/certs

# 日志级别:warn 仅记录错误和警告,避免日志爆炸
ModPagespeedMessageBufferSize 100000
ModPagespeedLogDir /var/log/httpd/pagespeed
ModPagespeedStatistics on
ModPagespeedStatisticsLogging on
ModPagespeedRewriteLevel CoreFilters

# --- 核心过滤器(按优先级排序)---
ModPagespeedEnableFilters collapse_whitespace,remove_comments,elide_attributes
ModPagespeedEnableFilters convert_png_to_jpeg,convert_jpeg_to_webp
ModPagespeedEnableFilters rewrite_css,rewrite_javascript,rewrite_images
ModPagespeedEnableFilters prioritize_critical_css,inline_google_font_css
ModPagespeedEnableFilters lazyload_images,defer_javascript

# --- 关键安全限制 ---
ModPagespeedFileCachePath "/var/cache/mod_pagespeed/"
ModPagespeedFileCacheSizeKb 102400
ModPagespeedFileCacheCleanIntervalMs 3600000
ModPagespeedFileCacheInodeLimit 500000

ModPagespeedImageMaxRewritesAtOnce 8
ModPagespeedCssInlineMaxBytes 2048
ModPagespeedJsInlineMaxBytes 2048
ModPagespeedImageResolutionLimitBytes 16777216

这份配置的关键设计逻辑如下:

  • RewriteLevel CoreFilters 是起点,它启用了 12 个最安全、副作用最小的过滤器(如空格压缩、注释移除)。它不碰 HTML 结构,不重写资源 URL,是“零风险”的基线。
  • EnableFilters 按组启用 ,而非全量开启。例如 convert_jpeg_to_webp 必须配合 inline_google_font_css (内联 Google Fonts CSS 可减少 DNS 查询),因为 WebP 转换依赖网络请求,而字体 CSS 内联能加速首屏。
  • 所有 MaxBytes LimitBytes 参数都设为显式值 。这是血泪教训:默认情况下 CssInlineMaxBytes 是 0(无限制),一旦某个页面引入了 5MB 的 bootstrap.css ,mod_pagespeed 会试图将其全部内联进 HTML 头部,导致响应体暴涨,触发 Apache 的 LimitRequestBody 限制,最终返回 400 错误。

3.2 虚拟主机级覆盖:为不同站点定制优化强度

假设你的云服务器上托管了两个站点: www.example.com (面向公众的企业官网)和 admin.example.com (内部后台系统)。前者需要极致优化,后者则要保证绝对稳定,禁用所有可能改变 DOM 结构的过滤器。

/etc/httpd/conf.d/vhost-example.conf 中,为 www.example.com 添加:

<VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /var/www/html/public

    # 继承全局配置,但增强图片优化
    ModPagespeed on
    ModPagespeedEnableFilters resize_images,strip_image_color_profiles
    ModPagespeedImageRecompressionQuality 85
    ModPagespeedJpegRecompressionQuality 82
</VirtualHost>

而为 admin.example.com ,则严格限制:

<VirtualHost *:443>
    ServerName admin.example.com
    DocumentRoot /var/www/html/admin
    SSLEngine on
    # ... SSL 配置省略 ...

    # 仅启用最保守的过滤器
    ModPagespeed on
    ModPagespeedRewriteLevel PassThrough
    ModPagespeedEnableFilters collapse_whitespace,remove_comments
    # 禁用所有可能重写资源的过滤器
    ModPagespeedDisableFilters rewrite_css,rewrite_javascript,rewrite_images
</VirtualHost>

这里 RewriteLevel PassThrough 是关键:它相当于“白名单模式”,只允许你 EnableFilters 中明确列出的过滤器生效,其他全部关闭。这比 DisableFilters 更安全,因为后者可能遗漏新版本新增的过滤器。

3.3 目录级微调:精准控制敏感路径

某些路径天生不适合优化,比如:

  • /api/ :返回 JSON,优化会破坏格式;
  • /uploads/ :用户上传的原始图片,不应被自动转 WebP;
  • /wp-admin/ (WordPress 后台):大量内联 JS 依赖特定执行顺序,重写可能导致功能异常。

在对应虚拟主机配置中加入:

<Location "/api/">
    ModPagespeed off
</Location>

<Location "/uploads/">
    ModPagespeedDisableFilters convert_jpeg_to_webp,convert_png_to_jpeg,resize_images
</Location>

# WordPress 后台专用规则
<Directory "/var/www/html/wp-admin/">
    ModPagespeed off
</Directory>

注意: <Location> <Directory> 的区别常被混淆。 <Location> 匹配 URL 路径(客户端请求的路径), <Directory> 匹配服务器文件系统路径。对于静态资源,用 <Location> ;对于需要读取磁盘文件的场景(如 WordPress 插件目录),用 <Directory> 更准确。

4. 验证不是“看网页变快了”,而是建立可观测性闭环

配置写完, systemctl restart httpd ,然后打开浏览器按 F5 —— 这种“肉眼验证”方式,在生产环境中等同于蒙眼开车。mod_pagespeed 提供了一套完整的可观测性接口,我们必须建立“配置 → 日志 → 指标 → 页面验证”的闭环,才能真正掌控优化效果。

4.1 启用 Statistics 页面:实时查看模块状态与命中率

mod_pagespeed 内置了一个 /pagespeed_admin/ 管理界面(默认仅限 localhost 访问)。在 pagespeed.conf 中添加:

<Location "/pagespeed_admin/">
    Require local
    # 如需远程访问(仅限调试),替换为:
    # Require ip 192.168.1.100
</Location>

重启 httpd 后,访问 http://localhost/pagespeed_admin/ ,你会看到一个类似 Nginx Stub Status 的实时面板,核心指标包括:

指标名 含义 健康阈值 异常表现
Pagespeed Score 当前页面优化得分(0-100) ≥ 85 < 70 说明关键过滤器未生效
Cache Hit Rate 文件缓存命中率 ≥ 95% < 90% 可能是 FileCachePath 权限错误或磁盘满
Rewrite Hits 每秒重写请求数 波动正常 突降至 0 表示模块未加载或 ModPagespeed off
Critical CSS Extraction Time 关键 CSS 提取耗时(ms) < 200ms > 500ms 可能是后端响应慢或 CriticalCssBeaconEnabled 未关

我曾在一个客户站点发现 Cache Hit Rate 长期卡在 42%,排查发现是 /var/cache/mod_pagespeed/ 目录属主为 root:root ,而 httpd 进程以 apache:apache 用户运行,导致无法写入缓存。执行 sudo chown -R apache:apache /var/cache/mod_pagespeed/ 后,命中率立刻升至 98.7%。

4.2 解析响应头:确认优化是否真实发生

浏览器开发者工具的 Network 面板,是验证优化效果的第一现场。但不要只看“Size”列,重点检查响应头:

  • X-Mod-Pagespeed : 存在即表示模块已加载并处理了该响应;
  • X-Page-Speed : 具体优化版本号(如 1.13.35.2-0 );
  • X-Content-Type-Options : 若为 nosniff ,说明 inline_google_font_css 生效;
  • Content-Encoding : 若为 br gzip ,说明 Brotli/Gzip 压缩已介入(mod_pagespeed 会接管压缩);
  • Vary : 若包含 Accept-Encoding, X-Forwarded-Proto, Upgrade-Insecure-Requests ,说明模块正确设置了缓存变体。

一个典型的优化后响应头示例:

HTTP/1.1 200 OK
Date: Mon, 06 May 2024 08:23:41 GMT
Server: Apache/2.4.59 (CentOS Stream)
X-Mod-Pagespeed: 1.13.35.2-0
X-Page-Speed: 1.13.35.2-0
Content-Encoding: br
Vary: Accept-Encoding, X-Forwarded-Proto, Upgrade-Insecure-Requests
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

如果 X-Mod-Pagespeed 头缺失,说明模块未加载(检查 httpd -M | grep pagespeed );如果存在但 Content-Encoding 仍是 identity ,说明 Brotli 模块未启用(需确认 mod_brotli 是否已加载)。

4.3 日志分析:从 pagespeed_messages 中定位具体问题

mod_pagespeed 的详细日志默认写入 /var/log/httpd/pagespeed/ ,但默认不启用。在 pagespeed.conf 中添加:

ModPagespeedMessageBufferSize 100000
ModPagespeedStatisticsLogging on
ModPagespeedLogDir /var/log/httpd/pagespeed

然后创建日志目录并授权:

sudo mkdir -p /var/log/httpd/pagespeed
sudo chown apache:apache /var/log/httpd/pagespeed
sudo chmod 755 /var/log/httpd/pagespeed

日志文件名为 pagespeed_messages ,内容为结构化文本。例如,当 convert_jpeg_to_webp 失败时,你会看到:

[0506/082341:INFO:pagespeed_http_value.cc(123)] Failed to convert image /var/www/html/uploads/photo.jpg to webp: Error 3 (libwebp: couldn't parse input)

这明确告诉你:是 photo.jpg 文件本身损坏,而非模块配置错误。这种粒度的日志,是快速定位问题的黄金线索。

4.4 Lighthouse 与 WebPageTest 对比:量化优化收益

最后,用第三方工具做客观验证。我推荐两个组合:

  • Lighthouse(Chrome DevTools) :运行 Performance 审计,重点关注 First Contentful Paint (FCP) Largest Contentful Paint (LCP) Cumulative Layout Shift (CLS) 三项。开启 mod_pagespeed 后,FCP 通常下降 30%-50%。
  • WebPageTest(www.webpagetest.org) :输入 URL,选择 “Dulles, VA - Chrome Desktop” 测试点,勾选 Capture Video 。对比开启前后的视频帧,你能清晰看到首屏元素“弹出”的时间差 —— 这比数字更直观。

我在一个电商详情页的测试中发现:开启 prioritize_critical_css 后,LCP 从 3.2s 降到 1.4s,但 CLS 却从 0.05 升到 0.18。进一步分析 WebPageTest 视频发现,是 inline_google_font_css 导致字体加载时机变化,引发文字重排。解决方案是关闭该过滤器,改用 font-display: swap 的 CSS 属性 —— 这再次证明: 自动化工具需要人工校准,而非盲目信任

5. 故障排查不是“重启服务”,而是遵循确定性诊断树

在云服务器上,mod_pagespeed 最常见的故障不是“不工作”,而是“部分工作”或“间歇性失效”。此时,凭经验瞎猜只会浪费时间。我总结了一套四层诊断树,每一步都有明确的命令和预期输出,已在 12 个不同客户的生产环境验证有效。

5.1 第一层:模块是否被 Apache 加载?

这是最基础也是最容易被忽略的一步。执行:

# 检查模块是否在加载列表中
httpd -M | grep pagespeed
# 正常输出: pagespeed_module (shared)

# 检查模块文件是否存在且可读
ls -l /usr/lib64/httpd/modules/mod_pagespeed.so
# 正常输出: -rwxr-xr-x. 1 root root 12345678 May  6 08:00 /usr/lib64/httpd/modules/mod_pagespeed.so

# 检查 SELinux 上下文是否正确
ls -Z /usr/lib64/httpd/modules/mod_pagespeed.so
# 正常输出: system_u:object_r:httpd_modules_type:s0 /usr/lib64/httpd/modules/mod_pagespeed.so

如果 httpd -M 无输出,说明 LoadModule 未正确写入配置;如果 ls -Z 显示 default_t 而非 httpd_modules_type ,说明 SELinux 签名失败。

5.2 第二层:配置语法是否通过 Apache 检查?

很多人修改 pagespeed.conf 后直接 systemctl restart httpd ,结果服务启动失败。正确做法是先做语法验证:

# 检查所有配置文件语法(包括 pagespeed.conf)
sudo httpd -t
# 输出: Syntax OK

# 如果报错,定位到具体行(例如)
# AH00526: Syntax error on line 42 of /etc/httpd/conf.d/pagespeed.conf:
# Invalid command 'ModPagespeedEnableFilters', perhaps misspelled or defined by a module not included in the server configuration

# 此时执行:
sudo httpd -M | grep pagespeed
# 若无输出,说明模块未加载,回到第一层

提示: httpd -t 不会检查过滤器名称是否拼写正确(如 convert_jpeg_to_webpp 多了个 p),但会检查指令是否存在。拼写错误会在日志中体现为 Unknown filter name

5.3 第三层:缓存与文件系统权限是否就绪?

mod_pagespeed 的 FileCachePath 是性能命脉。常见问题包括:

  • 目录不存在;
  • 目录权限不足(httpd 进程无法写入);
  • 磁盘空间不足( FileCacheSizeKb 设置过大);
  • 目录挂载为 noexec (禁止执行二进制)。

诊断命令:

# 检查目录是否存在且属主正确
sudo ls -ld /var/cache/mod_pagespeed/
# 正常输出: drwxr-xr-x. 3 apache apache 4096 May  6 08:00 /var/cache/mod_pagespeed/

# 检查磁盘空间(至少预留 20%)
df -h /var/cache/

# 检查挂载选项(排除 noexec)
mount | grep "$(df /var/cache | tail -1 | awk '{print $1}')"
# 正常输出中不应出现 noexec

如果发现权限问题,一键修复:

sudo mkdir -p /var/cache/mod_pagespeed/
sudo chown -R apache:apache /var/cache/mod_pagespeed/
sudo chmod 755 /var/cache/mod_pagespeed/

5.4 第四层:网络与后端依赖是否通畅?

mod_pagespeed 的部分过滤器(如 prioritize_critical_css inline_google_font_css )需要发起 HTTP 请求来获取外部资源。如果服务器无法访问公网,或防火墙拦截了出站连接,这些过滤器会超时失败。

验证方法:

# 以 apache 用户身份测试能否访问 Google Fonts(模拟模块行为)
sudo -u apache curl -I https://fonts.googleapis.com/css?family=Roboto
# 应返回 HTTP/2 200 OK

# 测试能否访问自身域名(Critical CSS 提取需要)
sudo -u apache curl -I http://localhost/
# 应返回 HTTP/1.1 200 OK

# 检查 httpd 错误日志中的超时记录
sudo tail -50 /var/log/httpd/error_log | grep -i "timeout\|failed\|critical"

如果 curl 测试失败,检查服务器的出站代理设置( http_proxy 环境变量)或防火墙规则( sudo firewall-cmd --list-all )。

6. 进阶技巧:让 mod_pagespeed 成为你运维体系的一部分

当 mod_pagespeed 在你的云服务器上稳定运行后,下一步不是“放着不管”,而是把它深度集成进你的运维流水线。以下是我在多个客户环境落地的 3 个实战技巧,它们不增加复杂度,却极大提升了可维护性。

6.1 配置版本化:用 Git 管理 /etc/httpd/conf.d/ 目录

/etc/httpd/conf.d/ 是 Apache 的“配置插件目录”,所有 .conf 文件都会被自动加载。我们将整个目录初始化为 Git 仓库:

# 进入配置目录
cd /etc/httpd/conf.d/

# 初始化 Git(首次)
sudo git init
sudo git config user.name "sysadmin"
sudo git config user.email "admin@example.com"

# 添加所有 conf 文件(排除临时文件)
sudo git add *.conf
sudo git commit -m "Initial commit: base Apache config + pagespeed"

# 后续每次修改 pagespeed.conf,都执行:
sudo git add pagespeed.conf
sudo git commit -m "pagespeed: enable lazyload_images for /blog/"
sudo git push origin main  # 推送到私有 Git 服务器

这样做的好处是:

  • 任何配置变更都有完整历史, git blame pagespeed.conf 可立刻定位是谁、何时、为何修改了某行;
  • 新服务器上线时,只需 git clone 配置仓库, cp *.conf /etc/httpd/conf.d/ ,即可获得完全一致的环境;
  • 审计时, git log --oneline -n 20 一页纸就能展示半年来的所有配置演进。

6.2 自动化健康检查:用 Cron 每 5 分钟探测模块状态

创建 /usr/local/bin/check-pagespeed.sh

#!/bin/bash
# 检查 mod_pagespeed 是否健康
URL="http://localhost/"
HEADER=$(curl -sI "$URL" 2>/dev/null | head -1)
if echo "$HEADER" | grep -q "X-Mod-Pagespeed"; then
    echo "$(date): OK - mod_pagespeed active" >> /var/log/pagespeed-health.log
    exit 0
else
    echo "$(date): CRITICAL - mod_pagespeed missing header" >> /var/log/pagespeed-health.log
    # 发送告警(此处用邮件,你可替换为钉钉/企业微信 webhook)
    echo "mod_pagespeed down on $(hostname)" | mail -s "ALERT: pagespeed down" admin@example.com
    exit 1
fi

添加到 crontab:

# 每 5 分钟执行一次
*/5 * * * * /usr/local/bin/check-pagespeed.sh

这个脚本简单粗暴,但极其有效。它不检查性能,只检查“模块是否活着”,是监控体系中最底层的“心跳”。

6.3 安全加固:禁用危险过滤器,防止 DoS 攻击

mod_pagespeed 的某些过滤器在恶意构造的请求下,可能成为 DoS 攻击入口。例如:

  • rewrite_images :对超大 PNG(>100MB)进行重采样,会耗尽 CPU 和内存;
  • prioritize_critical_css :对含大量 @import 的 CSS 进行递归解析,可能陷入死循环。

因此,我在所有生产环境的 pagespeed.conf 中,强制禁用高风险过滤器:

# 禁用所有可能被滥用的过滤器
ModPagespeedDisableFilters rewrite_images,prioritize_critical_css,inline_google_font_css,insert_dns_prefetch

# 仅允许经过充分测试的安全过滤器
ModPagespeedEnableFilters collapse_whitespace,remove_comments,elide_attributes,convert_jpeg_to_webp,convert_png_to_jpeg,defer_javascript,lazyload

更多推荐