· 6 min read ·

Why HTTP Frontend Code Is the Strongest Argument for Rust

Source: lobsters

NearlyFreeSpeech.net (NFSN) is a small, long-running web hosting provider that has operated since 2003 on a pay-per-use model unusual in shared hosting. They run FreeBSD, maintain their own billing and member interface code, and have historically been unusually transparent about their infrastructure decisions. Their recent post describing a complete rewrite of their C++ HTTP frontend infrastructure in Rust is worth reading closely, but the more interesting conversation is about why this particular category of code, HTTP frontend infrastructure, is one of the places where Rust’s value proposition lands hardest.

What “Frontend Infrastructure” Means for a Hosting Company

For a shared hosting provider, the frontend layer is the code that sits between the raw internet and everything else. It accepts TCP connections, terminates TLS, reads the incoming HTTP Host header (or TLS SNI extension) to identify which customer’s site is being requested, and then proxies the connection to the appropriate backend, whether that is a PHP-FPM process, a FastCGI handler, a static file server, or something else. This is not application code. It runs on every single request, touches raw bytes, and has to correctly handle the entire surface area of HTTP as actually used in the wild.

Many companies solve this with nginx or Caddy. NFSN, like several other infrastructure-minded organizations, had built their own in C++. The reasons are usually legitimate: custom routing logic, proprietary backend protocols, performance constraints that off-the-shelf software doesn’t meet, or institutional history from a time when the alternatives didn’t exist.

The Specific Problem With C++ HTTP Parsers

HTTP/1.1 is a textual protocol with decades of accumulated edge cases. The specification allows header field folding, a deprecated but technically valid feature where a header value continues on the next line if it begins with whitespace. It permits chunked transfer encoding to be combined with Content-Length in ways that are ambiguous between different implementations. The interaction between these ambiguities is the root cause of HTTP request smuggling, a class of attack where a frontend and backend server disagree on where one request ends and the next begins, allowing an attacker to poison request queues.

C++ HTTP parsers have a long and specific history of buffer overflows and off-by-one errors. Parsing text in C++ means managing the boundary between std::string_view and raw char pointers carefully, avoiding reads past the end of a buffer, and ensuring that every integer length value has been checked before use as an array index. These are mechanical tasks that do not benefit from programmer cleverness. A single missed bounds check can be a CVE.

The same applies to TLS stacks. OpenSSL’s Heartbleed vulnerability in 2014 was a two-line bounds checking mistake in C that exposed private keys from memory. BoringSSL and LibreSSL improved on OpenSSL’s codebase, but the fundamental issue is that the language provides no mechanism to enforce that buffer length arithmetic is correct at compile time.

What Rust Changes

Rust’s borrow checker enforces that every slice access is bounds-checked, either statically at compile time when the compiler can prove safety, or dynamically with a panic rather than undefined behavior. Buffer overreads cannot silently produce wrong results. This is a security property, not a convenience feature, and it eliminates an entire category of vulnerabilities without requiring garbage collection.

For HTTP parsing specifically, the Rust ecosystem has hyper, which underpins both the AWS SDK and many Cloudflare services. Its HTTP/1.1 parser is derived from nginx’s parser, extensively tested against the corpus of malformed requests that have historically triggered vulnerabilities, and benefits from every library user filing issues and running fuzzing campaigns. Using hyper in 2026 means inheriting years of adversarial testing.

For TLS, rustls provides a complete TLS 1.2/1.3 implementation with no C dependencies. It was designed from scratch for memory safety and has undergone formal security audits. Cloudflare runs rustls in production. The Let’s Encrypt client Certbot recommends it. The argument that OpenSSL is more battle-tested is narrowing as rustls accumulates deployment hours at scale.

For async I/O, tokio gives a production-grade async runtime with an epoll/kqueue event loop, structured concurrency primitives, and no garbage collection pauses. Before C++20’s coroutines, the async story in C++ involved either threads with high memory overhead, callback-based designs with complex lifetime management, or bespoke event loops with no ecosystem interoperability. Even with C++20 coroutines, ecosystem support is far behind tokio’s maturity. A frontend proxy written with hyper, rustls, and tokio gets a foundation that has been stress-tested across hundreds of production deployments.

Why Small Teams Feel This More

Cloudflare’s Pingora project replaced nginx in their global proxy fleet with a Rust proxy, citing CPU and memory savings alongside the ability to build custom features. They have a large team that could have sustained nginx maintenance indefinitely. For a small team like NFSN’s, the calculus is different. A small team maintaining C++ frontend infrastructure carries ongoing costs that compound over time:

  • Auditing every dependency update for memory safety issues
  • Running static analysis tools like clang-tidy and AddressSanitizer in staging, which catch some bugs but not all
  • Responding to security reports with the knowledge that the language provides no structural guarantee against entire classes of vulnerabilities
  • Onboarding new developers who must internalize years of implicit invariants that the compiler does not enforce

Rust shifts a substantial portion of that burden to compile time. The invariants live in the type system rather than in the developer’s working memory. A memory safety bug in production NFSN infrastructure could expose private keys or customer data across tenant boundaries; the cost of carrying that risk is real even when it never materializes.

The Rewrite Risk and How Rust Mitigates It

The standard objection to complete rewrites is captured in Joel Spolsky’s Things You Should Never Do from 2000, which argues that a rewrite discards years of bug fixes embedded in apparently ugly code. This is a genuine risk. The mitigation for infrastructure rewrites tends to be shadow testing: running the new implementation alongside the old one and comparing outputs on real traffic before cutover.

Rust specifically helps here because the type system forces the developer to make behavioral decisions explicit. When rewriting C++ that had implicit assumptions about string encoding or integer overflow, Rust will not compile until those assumptions are addressed. The rewrite surface area is smaller in practice because fewer bugs survive the type checker. Mozilla’s Stylo CSS engine in Firefox was reported to be more correct on day one than the C++ original at equivalent feature completion, and it has run in every Firefox installation since Firefox 57.

For cases where a full rewrite is too risky, the cxx crate by David Tolnay provides safe, zero-overhead interop between C++ and Rust across a defined interface boundary. A team can rewrite the HTTP parsing and TLS termination components first, keeping the routing logic in C++ temporarily, and then migrate the rest incrementally. NFSN apparently opted for a full rewrite, which is the higher-confidence path when the codebase is small enough to hold in one team’s head.

The Broader Context

NFSN’s rewrite joins a recognizable pattern across the industry. AWS Firecracker, the VMM behind Lambda and Fargate, was written in Rust from the start. The Linux kernel has accepted Rust for driver development since version 6.1. Android’s memory safety bugs, which historically accounted for roughly 70% of Android’s high-severity CVEs according to Google’s own tracking, have declined substantially in the components where Rust replaced C++. The data is accumulating that for code handling untrusted input at the systems level, memory-safe languages are an engineering choice, not an aesthetic one.

HTTP frontend infrastructure is not glamorous code. It is the thing that has to work every time, handle every malformed request without crashing, and never leak memory from one customer’s request into another’s response buffer. It is precisely the kind of code where Rust’s constraints are not frustrations to work around but structural guarantees that matter in ways the compiler can actually verify. NFSN’s rewrite is a reasonable template for any small infrastructure team still carrying that burden in C++.

Was this interesting?