修复:Discourse 在 iPhone 的 Facebook 应用内浏览器中渲染为未样式化的 HTML

摘要

如果您的 Discourse 站点在 iPhone 的 Facebook 应用中通过链接访问时显示为纯文本、无样式的 HTML(无 CSS、无 JavaScript、无功能),原因是 Discourse 的爬虫检测机制错误地将 Facebook 的应用内浏览器识别为机器人。

修复方法是通过 Rails 控制台进行一行代码的修改。


症状

用户从 iPhone 上的 Facebook 应用点击指向您 Discourse 站点的链接时,会看到一个精简的、无样式的 HTML 页面——本质上就是爬虫/无脚本布局。由于没有 JavaScript 运行,图片网格、灯箱和媒体播放器等功能无法使用。同样的链接在 Safari、Chrome 以及 Android 和 iPad 上的 Facebook 应用内浏览器中均能正常工作。


原因

iPhone 上 Facebook 的应用内浏览器在用户代理字符串中包含 facebook 一词,例如:

Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 
(KHTML, like Gecko) Mobile/23D8133 Safari/604.1 MetaIAB Facebook

Discourse 的爬虫检测(CrawlerDetection)会将用户代理与 crawler_user_agents 站点设置进行比对,该设置的默认值包含 facebook

rss|bot|spider|crawler|facebook|archive|wayback|ping|...

这导致 Discourse 向 Facebook 的应用内浏览器提供静态爬虫布局,而不是完整的 Ember 应用——尽管这实际上是一个真实用户使用的真实浏览器。

您可以通过检查 nginx 访问日志中来自该浏览器的请求来确认此情况,并注意到响应负载大小明显小于正常值(通常约为 5KB,而完整主题页面约为 35KB)。


修复方法

MetaIAB 添加到 crawler_check_bypass_agents 站点设置中。该设置专门用于豁免即使匹配爬虫列表的用户代理,使其不受爬虫处理。

注意: crawler_check_bypass_agents 是一个隐藏的站点设置,不会出现在标准管理界面中。必须使用 Rails 控制台。

通过 Rails 控制台

SiteSetting.crawler_check_bypass_agents = "MetaIAB"

如果该设置已有值(例如 cubot),请使用管道符分隔符进行追加:

SiteSetting.crawler_check_bypass_agents = "cubot|MetaIAB"

更改立即生效,无需重启。


为什么这样有效

CrawlerDetection.crawler? 方法结合使用以下三个设置:

  1. non_crawler_user_agents —— 如果用户代理匹配此列表,它可能是真实浏览器
  2. crawler_user_agents —— 如果同时匹配此列表,则被视为爬虫
  3. crawler_check_bypass_agents —— 如果匹配此列表,则无论其他条件如何,均豁免爬虫处理

Facebook 应用内浏览器的用户代理包含 Safari,匹配 non_crawler_user_agents;同时也包含 facebook,匹配 crawler_user_agents。将 MetaIAB(Facebook 应用内浏览器用户代理中独有的字符串)添加到 crawler_check_bypass_agents 后,Discourse 会向其提供完整的应用程序。


为什么 Android 和 iPad 不受影响

  • Android:Facebook 在 Android 上的应用内浏览器发送的用户代理不包含 facebook,因此顺利通过爬虫检测。
  • iPad:Facebook 在 iPad 上的应用内浏览器也会触发爬虫布局,但由于 iPad 报告的 window.innerWidth 较宽(约 1180px),Discourse 会提供桌面布局,而该布局恰好能正常渲染。iPhone 的窄视口(约 414px)会触发移动布局,而在爬虫模式下,该布局完全无法使用。

附加说明:meta-webindexer 泛滥

另外,Facebook 的网络索引器(meta-webindexer/1.1)可能会向您的站点发送极高频率的请求——可能每小时数千次——且全部指向首页。与 meta-externalagent(负责处理 OG 链接预览,应保持不阻塞)不同,meta-webindexer 对大多数 Discourse 安装似乎没有任何实用价值。

如果您在日志中观察到此类流量,可以通过将其添加到 nginx 的机器人阻止配置中,在 nginx 层面进行拦截:

"~*meta-webindexer" 1;

meta-externalagent 应保持允许,因为它负责在 Facebook 上分享链接时抓取 OG 元数据。

2 个赞

感谢 @shortmort37。我已提交一个拉取请求(PR),以更改核心中的默认设置,使其包含 MetaIAB:

5 个赞