{"article":{"slug":"cloudflare-is-rewriting-everything-in-rust","title":"Cloudflare is rewriting everything in Rust","subtitle":null,"summary":"Sylvain Kerkour traces Cloudflare's shift from C and Lua NGINX modules to Rust after CloudBleed, covering Pingora, FL2 and the NGINX termination rewrite, a critique of its open-source posture, and how avoiding .unwrap() could have prevented the November 18, 2025 outage.","content_type":"opinion","language":"en","canonical_url":"https://kerkour.com/rust-at-cloudflare","author":{"name":"Sylvain Kerkour","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Sylvain Kerkour","url":"https://kerkour.com/","listing_slug":null,"listing":null},"topics":[{"name":"Rust","slug":"rust","url":"https://listedarticles.com/topics/rust"},{"name":"Infrastructure","slug":"infrastructure","url":"https://listedarticles.com/topics/infrastructure"},{"name":"Security","slug":"security","url":"https://listedarticles.com/topics/security"},{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1102,"reading_minutes":5,"published_at":"2026-10-21T06:00:00.000Z","added_at":"2026-10-22T11:13:33.691Z","updated_at":"2026-10-22T11:13:33.691Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/cloudflare-is-rewriting-everything-in-rust","markdown_url":"https://listedarticles.com/articles/cloudflare-is-rewriting-everything-in-rust.md","example":false,"citation":"Sylvain Kerkour, Sylvain Kerkour. \"Cloudflare is rewriting everything in Rust.\" 21 Oct 2026. https://kerkour.com/rust-at-cloudflare (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://kerkour.com/rust-at-cloudflare"},"body_markdown":"# Cloudflare is rewriting everything in Rust\n\nBy Sylvain Kerkour, October 21, 2026\n\nOn February 17, 2017, a security researcher at Google found that Cloudflare reverse proxies were leaking HTTP requests traversing their network, the famous CloudBleed vulnerability.\n\nFor example, let's say that you were visiting `https://example.com`, hosted on Cloudflare, the HTTP response served by Cloudflare may have contained HTML content or HTTP headers for completely unrelated websites also hosted on Cloudflare. Personally Identifiable Information (PII), sensitive cookies, API keys and secret tokens were spread all over the internet. What made this issue particularly bad is that search engines and other bots were indexing and archiving all these secrets.\n\nThe issue came from transpiled C code that was used to parse HTML. Writing parsers is hard, writing C is hard, as virtually all infosec people know shipping parsers in C is the perfect recipe for remote exploits.\n\nAt the time, Cloudflare was principally built as NGINX modules in C and Lua, which, without surprise, led to memory safety problems.\n\nIn February 2017, Rust, a \"new\" programming language promising memory safety, high performance and great C interoperability, released its version 1.15.\n\nI'm not in Cloudflare's engineers and leadership heads, but the earliest post about Rust on Cloudflare's blog I can find is from January 2018. Probably not a coincidence.\n\nSince then, Cloudflare has been on a relentless course of rewriting its proxy services in Rust, first by writing NGINX modules in Rust and then by replacing NGINX itself with Rust-based proxies and services.\n\n*(Diagram: Cloudflare proxies architecture, credit: Cloudflare)*\n\nIn the diagram above, Pingora is already in Rust, FL is being gradually replaced by FL2 (FL version 2) which is in Rust, and the \"HTTP & TLS Termination\" layer, which is currently NGINX, is being rewritten... in Rust.\n\nRust is eating Cloudflare's proxy layer and they are replacing C code as fast as they can. On the other hand, the developer platform and API layer are developed in a mix of Go, TypeScript and probably a few other languages.\n\nOne of the many (unfair) advantages provided by Rust, is how easy it is to compile it to a C library to be used in other programming languages via FFI. For example, Cloudflare uses a Rust library in the proxy layer to match requests with rules, wirefilter (e.g. `if http.user_agent == 'xxx' -> Block Request`), and embeds the exact same library in the Go backend powering the API. Code once, use everywhere.\n\nPingora is Cloudflare's framework for \"building fast, reliable and evolvable network services\" which is Open Source.\n\nIt has been a huge boost in performance compared to the previous generation of NGINX-based proxies due to, among other things, better connections pooling to origins (the servers hosted behind Cloudflare).\n\nDespite some outages, it's safe to say that Rust has been a huge reliability increase, a huge velocity boost, a huge performance win and therefore a huge costs saver for the company.\n\nThis is what happens when the leadershhip of tech companies understand (at least a little bit) tech.\n\n## A note on Cloudflare and Open Source\n\nLike many big tech companies, Cloudflare has a pretty active GitHub, but don't be fooled, they are far from an Open Source company. As illustrated by this issue, some (most?) of their GitHub repositories are actually mirrors of their internal repositories and are pushed whenever they want, or can.\n\nWe can applaud the effort, but don't expect any kind of support or Open Source governance if your own projects rely on theirs.\n\n## November 18, 2025 outage\n\nLess than 24 hours after the outage, Cloudflare already had published a detailed post-mortem, signed by the CEO himself. Bravo!\n\nTL;DR: `.unwrap()` was called on a `Result` that was (wrongly) expected to never be `Err` due to configuration data that was (wrongly) assumed to never be malformed. Oops, sorry, beginner mistake. Always remember the first rule of programming: never trust unverified input.\n\nSo, how could it have been avoided?\n\n## Avoiding unwrap in Rust, or how the outage could have been prevented\n\nIn Rust, `.unwrap()` can be used on both `Result<T, E>` and `Option<T>`. If the value is `Err` or `None`, then the program panics.\n\nIn Cloudflare's case, `.unwrap()` was called on a `Result` with a `Err` value, which is bad! You should never call `.unwrap()` on a `Result`, especially when processing untrusted input (here a malformed configuration file), whatever your assumptions are. `Err` values should either be handled in place, or bubbled up to somewhere else.\n\nOn the other hand, it's not uncommon for Rust developers to `.unwrap()` on `Option` when they know (or think they know) that the value is `Some(T)`, which is not a bad practice per se, but Rust gives us many tools to free our code of `.unwrap()` and thus make it more reliable, otherwise we are back to null-dereference bugs plaguing other languages.\n\nHere is some code that I was often writing (not so) long time ago:\n\n```rust\nfn get_user_opt() -> Option<User> {\n    // ...\n}\n\nfn do_something() {\n    let user_opt = get_user_opt();\n    if user_opt.is_none() {\n        // do something else\n    }\n    let user = user_opt.unwrap();\n    // do something\n}\n```\n\n### .ok_or() / .ok_or_else()\n\nThe first technique is to use `.ok_or` to transform an `Option` into a `Result::Err`:\n\n```rust\nfn do_something() -> Result<(), Error> {\n    let user = get_user_opt().ok_or(Error::UserNotFound)?;\n    // do something\n}\n```\n\n### match\n\nThen, of course, there is `match`:\n\n```rust\nfn do_something() -> Result<(), Error> {\n    let user = match get_user_opt() {\n        Some(user) => user,\n        None => return Err(Error::UserNotFound);\n    };\n    // do something\n}\n```\n\n### if let / let else\n\nEven better, we now have two awesome features: `let` pattern deconstruction, and `if let` expressions:\n\n```rust\nfn do_something() -> Result<(), Error> {\n    let Some(user) = get_user_opt() else {\n        return Err(Error::UserNotFound);\n    }\n    // do something\n}\n```\n\n```rust\nfn do_something() -> Result<(), Error> {\n    if let Some(user) = get_user_opt() {\n        // do something\n    } else {\n        return Err(Error::UserNotFound);\n    }\n}\n```\n\n`.unwrap()` is a code smell and should be avoided at all costs. If you really, really need to `.unwrap()`, at least use `.expect(\"with an easy-to-grep message\")`.\n\n## Some closing thoughts\n\nAs I wrote previously, Rust is eating the world. It's the first full-stack programming language that can be used from bare-metal micro-controllers to cloud reverse proxies running on huge servers in data centers, like in Cloudflare's case.\n\nCloudflare, Google, and my own Pingoo all understand the unfair advantages provided by Rust enabling them to move fast and build vertically-integrated businesses that would only have been possible before with a mashup of many different programming languages and a huge number of developers maintaining fragile piles of mud.\n\n*Read the full post, including the author's further reading and book links, at [kerkour.com](https://kerkour.com/rust-at-cloudflare).*\n","body_html":"<h1 id=\"cloudflare-is-rewriting-everything-in-rust\">Cloudflare is rewriting everything in Rust</h1>\n<p>By Sylvain Kerkour, October 21, 2026</p>\n<p>On February 17, 2017, a security researcher at Google found that Cloudflare reverse proxies were leaking HTTP requests traversing their network, the famous CloudBleed vulnerability.</p>\n<p>For example, let&#39;s say that you were visiting <code>https://example.com</code>, hosted on Cloudflare, the HTTP response served by Cloudflare may have contained HTML content or HTTP headers for completely unrelated websites also hosted on Cloudflare. Personally Identifiable Information (PII), sensitive cookies, API keys and secret tokens were spread all over the internet. What made this issue particularly bad is that search engines and other bots were indexing and archiving all these secrets.</p>\n<p>The issue came from transpiled C code that was used to parse HTML. Writing parsers is hard, writing C is hard, as virtually all infosec people know shipping parsers in C is the perfect recipe for remote exploits.</p>\n<p>At the time, Cloudflare was principally built as NGINX modules in C and Lua, which, without surprise, led to memory safety problems.</p>\n<p>In February 2017, Rust, a &quot;new&quot; programming language promising memory safety, high performance and great C interoperability, released its version 1.15.</p>\n<p>I&#39;m not in Cloudflare&#39;s engineers and leadership heads, but the earliest post about Rust on Cloudflare&#39;s blog I can find is from January 2018. Probably not a coincidence.</p>\n<p>Since then, Cloudflare has been on a relentless course of rewriting its proxy services in Rust, first by writing NGINX modules in Rust and then by replacing NGINX itself with Rust-based proxies and services.</p>\n<p><em>(Diagram: Cloudflare proxies architecture, credit: Cloudflare)</em></p>\n<p>In the diagram above, Pingora is already in Rust, FL is being gradually replaced by FL2 (FL version 2) which is in Rust, and the &quot;HTTP &amp; TLS Termination&quot; layer, which is currently NGINX, is being rewritten... in Rust.</p>\n<p>Rust is eating Cloudflare&#39;s proxy layer and they are replacing C code as fast as they can. On the other hand, the developer platform and API layer are developed in a mix of Go, TypeScript and probably a few other languages.</p>\n<p>One of the many (unfair) advantages provided by Rust, is how easy it is to compile it to a C library to be used in other programming languages via FFI. For example, Cloudflare uses a Rust library in the proxy layer to match requests with rules, wirefilter (e.g. <code>if http.user_agent == &#39;xxx&#39; -&gt; Block Request</code>), and embeds the exact same library in the Go backend powering the API. Code once, use everywhere.</p>\n<p>Pingora is Cloudflare&#39;s framework for &quot;building fast, reliable and evolvable network services&quot; which is Open Source.</p>\n<p>It has been a huge boost in performance compared to the previous generation of NGINX-based proxies due to, among other things, better connections pooling to origins (the servers hosted behind Cloudflare).</p>\n<p>Despite some outages, it&#39;s safe to say that Rust has been a huge reliability increase, a huge velocity boost, a huge performance win and therefore a huge costs saver for the company.</p>\n<p>This is what happens when the leadershhip of tech companies understand (at least a little bit) tech.</p>\n<h2 id=\"a-note-on-cloudflare-and-open-source\">A note on Cloudflare and Open Source</h2>\n<p>Like many big tech companies, Cloudflare has a pretty active GitHub, but don&#39;t be fooled, they are far from an Open Source company. As illustrated by this issue, some (most?) of their GitHub repositories are actually mirrors of their internal repositories and are pushed whenever they want, or can.</p>\n<p>We can applaud the effort, but don&#39;t expect any kind of support or Open Source governance if your own projects rely on theirs.</p>\n<h2 id=\"november-18-2025-outage\">November 18, 2025 outage</h2>\n<p>Less than 24 hours after the outage, Cloudflare already had published a detailed post-mortem, signed by the CEO himself. Bravo!</p>\n<p>TL;DR: <code>.unwrap()</code> was called on a <code>Result</code> that was (wrongly) expected to never be <code>Err</code> due to configuration data that was (wrongly) assumed to never be malformed. Oops, sorry, beginner mistake. Always remember the first rule of programming: never trust unverified input.</p>\n<p>So, how could it have been avoided?</p>\n<h2 id=\"avoiding-unwrap-in-rust-or-how-the-outage-could-have-been-preven\">Avoiding unwrap in Rust, or how the outage could have been prevented</h2>\n<p>In Rust, <code>.unwrap()</code> can be used on both <code>Result&lt;T, E&gt;</code> and <code>Option&lt;T&gt;</code>. If the value is <code>Err</code> or <code>None</code>, then the program panics.</p>\n<p>In Cloudflare&#39;s case, <code>.unwrap()</code> was called on a <code>Result</code> with a <code>Err</code> value, which is bad! You should never call <code>.unwrap()</code> on a <code>Result</code>, especially when processing untrusted input (here a malformed configuration file), whatever your assumptions are. <code>Err</code> values should either be handled in place, or bubbled up to somewhere else.</p>\n<p>On the other hand, it&#39;s not uncommon for Rust developers to <code>.unwrap()</code> on <code>Option</code> when they know (or think they know) that the value is <code>Some(T)</code>, which is not a bad practice per se, but Rust gives us many tools to free our code of <code>.unwrap()</code> and thus make it more reliable, otherwise we are back to null-dereference bugs plaguing other languages.</p>\n<p>Here is some code that I was often writing (not so) long time ago:</p>\n<pre><code class=\"language-rust\">fn get_user_opt() -&gt; Option&lt;User&gt; {\n    // ...\n}\n\nfn do_something() {\n    let user_opt = get_user_opt();\n    if user_opt.is_none() {\n        // do something else\n    }\n    let user = user_opt.unwrap();\n    // do something\n}</code></pre>\n<h3 id=\"ok-or-ok-or-else\">.ok_or() / .ok_or_else()</h3>\n<p>The first technique is to use <code>.ok_or</code> to transform an <code>Option</code> into a <code>Result::Err</code>:</p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\n    let user = get_user_opt().ok_or(Error::UserNotFound)?;\n    // do something\n}</code></pre>\n<h3 id=\"match\">match</h3>\n<p>Then, of course, there is <code>match</code>:</p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\n    let user = match get_user_opt() {\n        Some(user) =&gt; user,\n        None =&gt; return Err(Error::UserNotFound);\n    };\n    // do something\n}</code></pre>\n<h3 id=\"if-let-let-else\">if let / let else</h3>\n<p>Even better, we now have two awesome features: <code>let</code> pattern deconstruction, and <code>if let</code> expressions:</p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\n    let Some(user) = get_user_opt() else {\n        return Err(Error::UserNotFound);\n    }\n    // do something\n}</code></pre>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\n    if let Some(user) = get_user_opt() {\n        // do something\n    } else {\n        return Err(Error::UserNotFound);\n    }\n}</code></pre>\n<p><code>.unwrap()</code> is a code smell and should be avoided at all costs. If you really, really need to <code>.unwrap()</code>, at least use <code>.expect(&quot;with an easy-to-grep message&quot;)</code>.</p>\n<h2 id=\"some-closing-thoughts\">Some closing thoughts</h2>\n<p>As I wrote previously, Rust is eating the world. It&#39;s the first full-stack programming language that can be used from bare-metal micro-controllers to cloud reverse proxies running on huge servers in data centers, like in Cloudflare&#39;s case.</p>\n<p>Cloudflare, Google, and my own Pingoo all understand the unfair advantages provided by Rust enabling them to move fast and build vertically-integrated businesses that would only have been possible before with a mashup of many different programming languages and a huge number of developers maintaining fragile piles of mud.</p>\n<p><em>Read the full post, including the author&#39;s further reading and book links, at <a href=\"https://kerkour.com/rust-at-cloudflare\" rel=\"nofollow ugc noopener\">kerkour.com</a>.</em></p>","headings":[{"level":1,"text":"Cloudflare is rewriting everything in Rust","id":"cloudflare-is-rewriting-everything-in-rust"},{"level":2,"text":"A note on Cloudflare and Open Source","id":"a-note-on-cloudflare-and-open-source"},{"level":2,"text":"November 18, 2025 outage","id":"november-18-2025-outage"},{"level":2,"text":"Avoiding unwrap in Rust, or how the outage could have been prevented","id":"avoiding-unwrap-in-rust-or-how-the-outage-could-have-been-preven"},{"level":3,"text":".ok_or() / .ok_or_else()","id":"ok-or-ok-or-else"},{"level":3,"text":"match","id":"match"},{"level":3,"text":"if let / let else","id":"if-let-let-else"},{"level":2,"text":"Some closing thoughts","id":"some-closing-thoughts"}]}}