HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
cloudflare
lol-html
Rust2019–2024
cloudflare / lol-html

lol-html

Low output latency streaming HTML parser/rewriter with CSS selector-based API

6Distinct people
6Mentions
0Explicit recommenders
2.1KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Low output latency streaming HTML parser/rewriter with CSS selector-based API

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 1:49:46 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year6 dated mentions

Observed mentions: 2019-11-29 11:01:33 UTC to 2024-05-27 11:40:15 UTC.

0122019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20212022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2024

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC110
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC000
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC202
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC101
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC110
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC101

What Hacker News says

6 sources
In response to this incident Cloudflare has made big engineering changes, including huge work to move away from C as much as possible. The offending parsers were rewritten in Rust (https://github.com/cloudflare/lol-html), as well as WAF, image optimization, and a few others. Nginx is being replaced with a custom cache server. New implementations are using either the Workers platform, or are written in Rust or Golang.
In Cloudflare took down our website

Ask HN: A fast, Rust HTML parser that works?

So I'm doing some web scraping in Rust, and so I will need to parse HTML. [scraper](https://docs.rs/scraper/latest/scraper/) (which uses [html5ever](https://github.com/servo/html5ever)) is doing fine except that it's the bottleneck of my application. So I need a faster parser. I've tried [tl](https://docs.rs/tl/latest/tl/) which would've been perfect except that it doesn't actually work on the HTML I have. When I try to `query_selector` the elements I need, it returns nothing. [Kuchiki](https://docs.rs/kuchiki/latest/kuchiki/) is abandonded. I couldn't figure out how to get [lol-html](https://github.com/cloudflare/lol-html) to work for me (it's designed for re-writing HTML, whatever that means). It doesn't seem to have an API to extract the inner text of an element. [html5gum](https://github.com/untitaker/html5gum) seems to be just an HTM
Read the rest
L tokenizer, or otherwise just too low-level. I have not yet tried [quick-xml](https://github.com/tafia/quick-xml/) but judging from the README, it's pretty low-level too. I mean, if these are the only options left then I will try them. Otherwise, I would love to use a parser that's faster but as ergonomic as `scraper` or `tl`. At this point, I would be happy with an Lxml bridge/port of some sort. I don't need to mutate HTML, just parse and read data from it.
2 points · Open discussion
I apologize. I skimmed your post and the response and fit it into some stereotypes. Your question is actually a lot more interesting than I thought. I couldn't figure it out reading the docs on my phone. When I first tried to reproduce your issue, I got that `try_new` didn't exist. It's been removed in the latest version of lol_html. Replacing it with `new`, your issue didn't reproduce. I was able to reproduce with v0.2.0, though. Since the issue had to do with code generated by macros, I tried `cargo expand` (something you need to install, see [1]). Here's what `let handlers = ...` expanded to in v0.2.0: let handlers = <[_]>::into_vec(box [( &"a[href]".parse::<::lol_html::Selector>().unwrap(), ::lol_html::ElementContentHandlers::default().element(|el| { let href = el.get_attribute("href").unwrap().replace(
Read the rest
"http:", "https:"); el.set_attribute("href", &href).unwrap(); Ok(()) }), )]); and here's what it expands to in v0.3.0 let handlers = <[_]>::into_vec(box [( ::std::borrow::Cow::Owned("a[href]".parse::<::lol_html::Selector>().unwrap()), ::lol_html::ElementContentHandlers::default().element(|el| { let href = el.get_attribute("href").unwrap().replace("http:", "https:"); el.set_attribute("href", &href).unwrap(); Ok(()) }), )]); Ignore the first line, it's how the macro vec! expands. The second line shows the difference in what the versions generate. The first takes a borrow of the result of parse, the second takes a Cow::Owned of it. (Cow stands for copy on write, but it's more generally useful for anything where you want to be generic over either the borrowed or owned version of something.). So the short answer is the macro used to expand to something that wasn't owned, and now it does. As for why it worked without a separate assignment, that's because Rust automatically created a temporary variable for you. > When using a value expression in most place expression contexts, a temporary unnamed memory location is created initialized to that value and the expression evaluates to that location instead, except if promoted to a static https://doc.rust-lang.org/reference/expressions.html#tempora... Initially rust created multiple temporaries for you, all valid for the same-ish scope, the scope of the call to try_new. When you break out the vector to its own assignment the temporary created for element! is only valid for the scope of the vector assignment. I took a look at the git blame[2] for the element! macro in lol_html, and they made the change because someone opened an issue with essentially your problem. So I'd say this is a bug in a leaky abstraction, not an issue with your understanding of rust. [1]: https://github.com/dtolnay/cargo-expand [2]: https://github.com/cloudflare/lol-html/commit/e0eaf6c4234af8...
In Things you can’t do in Rust (and what to do instead)

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.