搜索全站
主题 11 分钟阅读

Gravatar 头像加载慢的解决:国内镜像替换与本地缓存

WordPress 头像转圈半天不出来?本文讲清 Gravatar 请求链路在国内的失败模式,给出镜像替换、本地缓存、默认头像兜底三段代码,并说明如何验证是否生效。

U
UFO_PF 2019-07-16 · 阅读 11 分钟
Gr 主题
阅读提示
本文约 4,294 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:评论区文字出来了,头像还在转圈

国内站点最典型的场景:文章正文和评论文字都渲染完了,评论区一堆灰色占位圈在慢慢转,有的干脆超时变成裂图。打开开发者工具的网络面板,能看到一堆挂在 secure.gravatar.com 上的请求,状态长期 pending,最后要么超时要么等上好几秒。这个问题和服务器配置无关,换再好的主机也没用——它是跨境网络链路的问题。

Gravatar 的头像服务在国内长期不稳定,0.gravatar.com、1.gravatar.com、2.gravatar.com、secure.gravatar.com 这几个域名都可能时好时坏,而且它还要做一次 302 跳转。更麻烦的是,一个请求失败并不会让页面报错,只会留下一个空洞,容易被误判成「主题样式问题」。本文给出三种方案:换成国内镜像、做本地缓存、给默认头像兜底。三者可以叠加,建议至少做前两件。

原理分析:一次头像请求经历了什么

get_avatar() 是主题里输出头像的入口,它内部调用 get_avatar_url() 拼出图片地址。URL 形如 https://secure.gravatar.com/avatar/<hash>?s=96&d=mm&r=g,其中 <hash> 是邮箱地址的 MD5。主题调用时只传尺寸和 alt,get_avatar() 返回完整的 <img> 标签。

关键介入点是两个过滤器:get_avatar_url(在 URL 拼接完成后触发,适合整体换域名)和 get_avatar(在完整 HTML 生成后触发,适合整体替换或缓存)。二者选其一即可,不要同时改域名又改 HTML,否则容易出现双重替换导致的畸形地址。

方案 改动位置 优点 风险
国内镜像 get_avatar_url 一行代码,全站生效 依赖第三方镜像可用性
本地缓存 get_avatar + transient 彻底摆脱外链 首次访问仍慢,需定时任务预热
默认头像兜底 avatar_defaults + 后台设置 失败时也不难看 个性化下降
直接关闭 后台「设置 → 讨论」取消显示头像 零成本 评论区观感明显变差

需要注意的是,「本地缓存」通常指的是缓存头像的 URL 或渲染结果,真正的图片二进制落地需要额外写抓取逻辑(用 wp_remote_get() 拉图 + wp_upload_bits() 存到媒体库)。对大多数站点,缓存渲染结果已经足够,因为浏览器会按 URL 缓存图片本身。另外可以在 <head> 里加 preconnect,提前完成 DNS 和 TLS 握手,这一层由 wp_resource_hints 过滤器控制。

保姆级实操

  1. 先确认瓶颈:开发者工具 Network 面板筛 gravatar,看 Waterfall 里 DNS / Initial connection / TLS 各占多少。若绝大部分时间花在连接建立上,换镜像或本地缓存才有效;若图片本身很大,应顺手把 avatar_size 调小。
  2. 换镜像:用 get_avatar_url 把几个 Gravatar 域名统一替换。国内可选的有 Cravatar(cravatar.cn)等镜像服务,接入前先自己 curl 一次确认可用。
  3. 加 preconnect:通过 wp_resource_hints 输出 dns-prefetch,让浏览器提前解析域名。
  4. 默认头像兜底:在 avatar_defaults 里注册一张存放在主题目录的本地图片,再到后台「设置 → 讨论 → 默认头像」选中它。这样即使外链全挂,评论区也不会出现裂图。
  5. 渲染结果缓存:对已登录用户的头像 HTML 做 transient 缓存,减少重复的字符串拼接与过滤器调用。
  6. 验证:清除浏览器缓存后刷新,Network 面板里的头像请求域名应已变更;再用在线 HTTP 检测工具从国内多节点测一次镜像域名的响应时间。
<?php
// 子主题 functions.php:把 Gravatar 域名替换成国内镜像
add_filter( 'get_avatar_url', function( $url, $id_or_email, $args ) {
    $from = array(
        'https://secure.gravatar.com',
        'https://0.gravatar.com',
        'https://1.gravatar.com',
        'https://2.gravatar.com',
        'http://0.gravatar.com',
        'http://1.gravatar.com',
        'http://2.gravatar.com',
    );
    return str_replace( $from, 'https://cravatar.cn', $url );
}, 10, 3 );
<?php
// 提前完成 DNS 解析,缩短首个头像请求的连接时间
add_filter( 'wp_resource_hints', function( $hints, $relation_type ) {
    if ( 'dns-prefetch' === $relation_type ) {
        $hints[] = 'https://cravatar.cn';
    }
    return $hints;
}, 10, 2 );
<?php
// 注册一张本地默认头像,外链失败时不出现裂图
add_filter( 'avatar_defaults', function( $defaults ) {
    $local = get_stylesheet_directory_uri() . '/assets/img/default-avatar.png';
    $defaults[ $local ] = '站点默认头像';
    return $defaults;
} );

// 头像 HTML 片段缓存(按邮箱哈希 + 尺寸缓存 15 天)
add_filter( 'get_avatar', function( $avatar, $id_or_email, $size ) {
    $email = '';
    if ( is_object( $id_or_email ) && ! empty( $id_or_email->comment_author_email ) ) {
        $email = $id_or_email->comment_author_email;
    } elseif ( is_string( $id_or_email ) && is_email( $id_or_email ) ) {
        $email = $id_or_email;
    }
    if ( ! $email ) {
        return $avatar;
    }
    $key  = 'mfav_' . md5( $email ) . '_' . (int) $size;
    $html = get_transient( $key );
    if ( false === $html ) {
        $html = $avatar;
        set_transient( $key, $html, 15 * DAY_IN_SECONDS );
    }
    return $html;
}, 10, 3 );
# 接入前先验证镜像可用性与响应时间(国内服务器执行)
curl -o /dev/null -s -w "connect:%{time_connect} total:%{time_total} code:%{http_code}\n" \
  "https://cravatar.cn/avatar/205e460b479e2e5b48aec07710c08d50?s=96"

常见踩坑

  • 改了过滤器却不生效:主题或缓存插件自己拼头像地址,不走 get_avatar()。先在主题里搜索 gravatar 关键字确认调用来源。
  • 同时用两个过滤器:get_avatar_url 换域名、get_avatar 又做一次字符串替换,域名被替换两次导致地址错误。
  • 缓存插件缓存了旧地址:get_avatar 的输出被整页缓存固化,换镜像后必须清一次页面缓存才看得到效果。
  • 忽略 HTTPS:镜像域名若不支持 HTTPS,而站点全站 HTTPS,浏览器会拦截混合内容,头像全部不显示。
  • 默认头像尺寸太小:后台设置的默认头像被拉伸成大尺寸时模糊,建议准备 96px 以上的正方形图。
  • 案例:一个外贸企业站在评论区上线后收到大量「头像打不开」的反馈,站长以为是 CDN 问题,把整站切到新的 CDN 后依旧。最后用 curl 从国内三地测试发现 secure.gravatar.com 平均连接时间超过 4 秒且间歇性超时。按本文替换镜像 + 本地默认头像兜底后,评论区加载时间回到正常水平,且即使镜像临时不可用也有本地图兜底,不再有裂图投诉。
  • 把头像缓存写进数据库却不清理:换了镜像域名后旧缓存仍指向旧地址,需要在上线时手动删一次 transient,做法参考 WordPress Transient 缓存清理。

总结

判断要不要处理头像,看 Network 面板里 gravatar 请求的 time_connect:超过 500ms 就值得换镜像,经常性超时就必须换。落地顺序建议「先加本地默认头像兜底(零风险)→ 再换镜像 → 最后做缓存」,每一步都可独立回滚。换镜像属于依赖第三方服务,上线后应当定期用 curl 抽查,避免镜像失效却无人知晓。评论区的整体加载优化见 WordPress 评论区加载优化,两者配合才能把爆款文章的评论体验稳住。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。