2025年11月18日协调世界时(本文中所有时间均为协调世界时)11:20,Cloudflare网络开始出现核心网络流量交付严重故障。互联网用户访问我们客户的网站时,会看到显示Cloudflare网络内部故障的错误页面。

事件期间显示的HTTP错误页面

此问题并非由任何类型的网络攻击或恶意活动直接或间接导致。相反,其触发原因是我们对某个数据库系统的权限进行了更改,导致该数据库向Bot管理系统使用的“特征文件”中输出了多条重复条目。这使得该特征文件的大小翻倍,随后这个超出预期大小的特征文件被分发到了我们网络中的所有设备。

这些设备上运行的用于在网络中路由流量的软件会读取该特征文件,以确保Bot管理系统能够及时应对不断变化的威胁。但该软件对特征文件的大小设置了限制,而翻倍后的文件大小超过了这一限制,导致软件运行失败。

起初,我们错误地认为观察到的症状是由超大规模DDoS攻击引起的,但随后我们正确识别了核心问题,并停止了该超大特征文件的分发,改用其早期版本替代。到14:30,核心流量基本恢复正常。在接下来的几个小时里,我们处理了流量恢复过程中网络各部分出现的负载激增问题。截至17:06,Cloudflare的所有系统均已恢复正常运行。

对于此次事件给我们的客户以及整个互联网带来的影响,我们深表歉意。鉴于Cloudflare在互联网生态系统中的重要性,任何系统中断都是不可接受的。我们的网络在一段时间内无法正常路由流量,这让团队的每一位成员都深感痛心。我们知道,今天我们让大家失望了。

本文将详细叙述事件的完整经过、相关系统和流程的失效点,同时也标志着我们将采取一系列措施以确保此类中断不再发生——这只是一个开始,而非结束。

故障情况

下图显示了Cloudflare网络返回的HTTP 5xx错误状态码数量。正常情况下,该数值应该非常低,在故障发生前也确实如此。

Cloudflare网络返回的HTTP 5xx请求数量

11:20之前的数值是我们网络中正常的5xx错误基准水平。图表中的峰值及后续波动表明,系统因加载错误的特征文件而出现故障。值得注意的是,系统会在一段时间后自行恢复,这种内部错误表现极为罕见。

经调查,该特征文件由ClickHouse数据库集群上的查询每五分钟生成一次,而我们当时正在逐步更新该集群以改进权限管理。只有当查询在已完成更新的集群节点上运行时,才会生成包含错误数据的文件。因此,每五分钟都有可能生成一组正常或异常的配置文件,并迅速分发到整个网络。

这种波动使得故障原因难以排查——网络会因分发到正常配置文件而恢复,随后又因分发到异常配置文件而再次失效。起初,这让我们误以为可能是受到了攻击。最终,所有ClickHouse节点都开始生成异常配置文件,波动停止,系统稳定在故障状态。

直到14:30开始识别并解决根本问题后,错误才停止出现。我们的解决方案是:停止生成和分发异常的特征文件,手动将已知正常的文件插入特征文件分发队列,并强制重启核心代理。

上图中后续的长尾部分是由于我们的团队正在重启所有进入异常状态的剩余服务,到17:06,5xx错误码数量恢复正常水平。

受影响的服务如下:

服务/产品

影响描述

核心CDN和安全服务

返回HTTP 5xx状态码。本文顶部的截图显示了向终端用户推送的典型错误页面。

Turnstile(验证码服务)

无法加载。

Workers KV(键值存储服务)

由于核心代理故障导致KV的“前端”网关请求失败,Workers KV的HTTP 5xx错误率显著升高。

控制台(Dashboard)

控制台基本可正常访问,但由于登录页面上的Turnstile服务不可用,大多数用户无法登录。

邮件安全(Email Security)

邮件处理和投递未受影响,但我们观察到暂时无法访问某个IP信誉源,导致垃圾邮件检测准确率下降,部分新域名年龄检测无法触发,未发现对客户造成严重影响。此外,部分自动移动(Auto Move)操作出现失败;所有受影响的邮件均已完成审核和修复。

访问控制(Access)

从事件开始到13:05启动回滚期间,大多数用户普遍遭遇身份验证失败。已有的访问会话不受影响。

所有身份验证失败都会显示错误页面,意味着这些用户在身份验证失败期间均无法访问目标应用。此期间的成功登录已正常记录。

在此期间尝试的任何访问控制配置更新要么直接失败,要么传播极为缓慢。目前所有配置更新均已恢复正常。

除返回HTTP 5xx错误外,我们还观察到故障期间CDN的响应延迟显著增加。这是因为我们的调试和可观测性系统会自动为未捕获的错误添加额外调试信息,导致CPU资源被大量占用。

Cloudflare的请求处理流程及此次故障的成因

所有发往Cloudflare的请求都会通过我们网络中一条明确的路径:可能来自浏览器加载网页、移动应用调用API或其他服务的自动化流量。这些请求首先在HTTP和TLS层终止,然后流入核心代理系统(内部称为“Frontline”,简称FL),最后通过Pingora执行缓存查询,或在需要时从源站获取数据。

我们之前在此处详细介绍了核心代理的工作原理。

反向代理架构图

当请求通过核心代理时,我们会运行网络中提供的各类安全和性能产品。代理会应用每个客户的独特配置和设置,包括执行WAF规则、DDoS防护、将流量路由至开发者平台和R2存储等。这一过程通过一组特定领域的模块实现,这些模块将配置和策略规则应用于通过代理的流量。

其中,Bot管理模块正是此次故障的根源。

Cloudflare的Bot管理服务包含多种系统,其中之一是机器学习模型,用于为通过网络的每个请求生成Bot评分。客户可利用Bot评分控制哪些Bot允许访问其网站(或禁止访问)。

该模型的输入是一个“特征”配置文件。此处的“特征”指机器学习模型用于判断请求是否为自动化流量的单个属性,而特征配置文件则是这些单个特征的集合。

该特征文件每几分钟更新一次并发布到整个网络,使我们能够快速响应互联网流量变化,应对新型Bot和Bot攻击。因此,为了及时跟上恶意攻击者的策略变化,该文件的频繁且快速分发至关重要。

生成该文件的底层ClickHouse查询行为发生了变化(下文将详细说明),导致文件中出现大量重复的“特征”行。这改变了原本固定大小的特征配置文件的尺寸,引发Bot模块触发错误。

结果,处理客户流量的核心代理系统向所有依赖Bot模块的流量返回了HTTP 5xx错误码,这也影响了依赖核心代理的Workers KV和Access服务。

与此次事件无关的是,我们目前正在将客户流量迁移到新版本的代理服务(内部称为FL2)。两个版本均受到了此次问题的影响,但表现出的影响有所不同:

  • 使用新版FL2代理引擎的客户:观察到HTTP 5xx错误。
  • 使用旧版FL代理引擎的客户:未出现错误,但Bot评分生成异常,所有流量的Bot评分均为0。配置了Bot拦截规则的客户会出现大量误判;未在规则中使用Bot评分的客户则未受影响。

另一个让我们误以为遭遇攻击的现象是:Cloudflare的状态页面(status page)无法访问。该状态页面完全部署在Cloudflare基础设施之外,与Cloudflare无任何依赖关系。尽管最终证实这只是巧合,但仍让部分排查故障的团队成员认为,攻击者可能同时针对我们的系统和状态页面。当时访问状态页面的用户会看到以下错误信息:

Cloudflare状态页面的错误信息

在内部故障沟通群中,我们担心这可能是近期一系列大规模Aisuru DDoS攻击的延续:

内部聊天截图

查询行为变化

如前所述,底层查询行为的变化导致特征文件包含大量重复行,该数据库系统采用ClickHouse软件。

为便于理解,先简要说明ClickHouse的分布式查询工作原理:ClickHouse集群由多个分片(shard)组成。为了从所有分片查询数据,我们在名为default的数据库中设置了所谓的分布式表(由Distributed表引擎提供支持)。Distributed引擎会查询r0数据库中的底层表,这些底层表是数据在ClickHouse集群各分片上的实际存储位置。

此前,分布式表的查询通过共享系统账户运行。为了提高分布式查询的安全性和可靠性,我们正在进行相关优化,使这些查询能够通过初始用户账户运行。

在此次事件之前,当ClickHouse用户从system.tablessystem.columns等系统表查询表元数据时,只能看到default数据库中的表。

由于用户本就对r0中的底层表拥有隐式访问权限,我们在11:05进行了一项更改,将这种访问权限显式化,使用户也能看到这些表的元数据。通过确保所有分布式子查询都能在初始用户权限下运行,查询限制和访问授权可以更精细地执行,避免单个用户的不良子查询影响其他用户。

上述更改使所有用户都能访问其有权限的表的准确元数据,但遗憾的是,过去的代码中存在一个假设:如下查询返回的列列表仅包含default数据库中的表:

SELECTname,typeFROM system.columnsWHEREtable = 'http_requests_features'order by name;

注意,该查询未对数据库名称进行过滤。随着我们逐步向ClickHouse集群的用户推出显式授权,11:05的更改后,上述查询开始返回“重复”列——这些列来自r0数据库中存储的底层表。

不幸的是,Bot管理特征文件生成逻辑正是通过此类查询来构建文件中的每个输入“特征”。

上述查询原本会返回如下列表格(简化示例):

代码块示例

但由于向用户授予了额外权限,查询响应现在包含了r0模式的所有元数据,最终导致响应行数增加了一倍多,进而影响了最终输出文件中的行数(即特征数量)。

内存预分配

我们代理服务上运行的每个模块都设置了多项限制,以避免无限制的内存消耗,并通过预分配内存来优化性能。在此次事件中,Bot管理系统对运行时可使用的机器学习特征数量设置了限制——当前限制为200个,远高于我们目前约60个的实际使用量。设置该限制的原因是为了性能优化,我们会为这些特征预分配内存。

当包含超过200个特征的异常文件分发到我们的服务器时,触发了该限制,导致系统崩溃。以下是FL2的Rust代码中进行检查的部分,也是未处理错误的来源:

生成错误的代码

这导致了以下崩溃,进而返回5xx错误:

thread fl2_worker_thread panicked: called Result::unwrap() on an Err value

事件期间的其他影响

所有依赖核心代理的其他系统在事件期间均受到影响,包括Workers KV和Cloudflare Access。团队在13:04通过为Workers KV部署核心代理旁路补丁,减轻了这些系统的影响。随后,所有依赖Workers KV的下游系统(如Access本身)的错误率均有所下降。

由于内部使用了Workers KV,且Cloudflare Turnstile作为登录流程的一部分部署,Cloudflare控制台也受到了影响。

Turnstile受此次故障影响,导致未拥有活跃控制台会话的客户无法登录。如下图所示,其可用性在两个时间段下降:11:30至13:10,以及14:40至15:30。

事件期间Cloudflare内部API的可用性

第一个时间段(11:30-13:10)的原因是Workers KV受影响,而部分控制平面和控制台功能依赖该服务。13:10,Workers KV启用核心代理旁路后,该问题得到解决。

控制台的第二次可用性下降发生在特征配置数据恢复之后。大量积压的登录尝试导致控制台不堪重负,再加上重试请求,造成延迟升高,可用性下降。通过扩展控制平面的并发处理能力,控制台在约15:30恢复正常可用性。

修复措施及后续计划

目前我们的系统已恢复正常运行,我们已开始着手强化系统,以防范未来类似故障的发生。具体措施包括:

  • 加强对Cloudflare生成的配置文件的接入校验,采用与用户生成输入相同的严格标准

  • 为各项功能启用更多全局紧急关闭开关(kill switch)

  • 避免核心转储(core dump)或其他错误报告占用过多系统资源

  • 审查所有核心代理模块的错误条件故障模式

此次是Cloudflare自2019年以来最严重的一次故障。我们过去曾发生过导致控制台不可用新功能暂时无法使用的故障,但在过去6年多的时间里,我们从未发生过导致大部分核心流量无法通过网络路由的严重中断。

像今天这样的故障是不可接受的。我们的系统设计初衷是具备极高的故障恢复能力,以确保流量始终能够正常流转。过去的每一次故障都促使我们构建更具韧性的系统。

我代表Cloudflare全体团队,就今天给互联网带来的影响深表歉意。

时间(协调世界时)

状态

描述

11:05

正常

部署数据库访问控制更改

11:28

故障开始

更改部署至客户环境,首次观察到客户HTTP流量出现错误

11:32-13:05

团队调查Workers KV服务的流量激增和错误问题

初始症状表现为Workers KV响应率下降,导致Cloudflare其他服务受到下游影响。

我们尝试了流量调控和账户限制等缓解措施,以恢复Workers KV服务的正常运行水平。

自动化测试在11:31首次检测到问题,人工排查于11:32开始,11:35启动故障应急会议。

13:05

实施Workers KV和Cloudflare Access旁路——影响减轻

排查过程中,我们为Workers KV和Cloudflare Access启用了内部系统旁路,使其回退到早期版本的核心代理。尽管该版本的代理也存在相同问题,但如前文所述,其影响范围更小。

13:37

重点推进Bot管理配置文件回滚至已知正常版本

我们确认Bot管理配置文件是此次事件的触发因素。团队通过多个工作流并行寻找服务修复方案,最快的方案是恢复该文件的历史正常版本。

14:24

停止新Bot管理配置文件的生成和分发

我们确定Bot管理模块是500错误的来源,且由异常配置文件导致。因此停止了Bot管理配置文件的自动部署。

14:24

新文件测试完成

我们观察到使用旧版本配置文件后系统成功恢复,随后开始加速全球范围内的修复进程。

14:30

主要故障解决,下游受影响服务错误率开始下降

正确的Bot管理配置文件在全球范围内部署,大多数服务恢复正常运行。

17:06

所有服务恢复正常,故障结束

所有下游服务重启完毕,所有操作完全恢复。