ESPN'This one hurts': How are Cowboys reacting to third straight 1-2 start?PunchAtiku faults FG over fresh $1.5bn World Bank loanThe Jerusalem PostLikud election candidate Bonzel says he wishes former hostage were 'returned to Hamas,' apologizesInquirerCamarines Sur placed under state of calamity over dengueESPN DeportesSemana 3 NFL: De la A a la Z, según el Gurú de las DiagonalesRadio TimesTop 10 Picks of the Day – Friday 2 OctoberScreen RantGod of War Laufey - Release Date, Preorders, Price, & PlatformsLa PresseAllégations de racisme au SPVM | Le dossier transmis au DPCPIl Sole 24 OreUstica, la Francia pronta a collaborare, pm verso ritiro archiviazioneسكاي نيوز عربيةمسؤول: محادثات أميركية إيرانية مرتقبة بوساطة قطريةFotogramas'Monstruo' recupera el vuelo en Netflix con una temporada 4 cautivadora, pero sigue lejos de la brillantez de 'Dahmer'SportstarVozinha, FIFA World Cup hero of Cape Verde, opens up consequences of fame after FWC 2026
The Daily Newsstand · Free, Always
Monday, September 28, 2026

Сжатый интернет с несжатым кешем: что Cloudflare нашла у себя на дисках

Translate

Смотрите, как выходит. Браузер в каждом запросе докладывает через Accept-Encoding, какие форматы сжатия понимает. Cloudflare жмет и отдает — этот кусок отлажен давно, работает очень даже неплохо, претензий вообще нет. А потом страница уезжает в кеш. И если origin прислал ее без Content-Encoding, то... ничего, собственно, не происходит. На диске она лежит как прислали, так же катается между дата-центрами.

По их подсчетам, такого текста в трафике набегает около 71%. HTML, JSON, CSS, JavaScript — как раз то, что жмется лучше всего. И это при том, что сжатие на веб-сервере включается буквально парой строк в конфиге.

1 сентября в блоге Cloudflare вышел пост о прототипе под названием Cache Transcoding. Ответ там сжимают с помощью Zstandard прямо при записи в кеш, дальше он хранится сжатым и между дата-центрами ездит тоже сжатым, а разворачивается только на отдаче клиенту. В тестах объекты ужались примерно в 2,8 раза, отсюда и обещанные петабайты освободившейся емкости. Пост написала Аши Патель, прототип она собрала на стажировке. В проде его пока нет, так как для выводов нужна выборка побольше.

Надо сказать, что хранить кеш сжатым придумали вовсе не в Cloudflare. Varnish делает так с 2011 года, а споры о том, вправе ли кеш вообще менять содержимое ответа, начались еще раньше. Просто выбор всегда стоял между диском и процессором, кроме того, диск был дешевле, поэтому платить процессорным временем за экономию места никто не хотел.

Идее этой лет пятнадцать

Хранить в кеше сжатое и распаковывать на выдаче придумали задолго до Cloudflare. В Varnish встроенное сжатие появилось еще в версии 3.0 в 2011 году. Флаг beresp.do_gzip в VCL велит сжать объект перед тем, как класть его в кеш; клиентам с поддержкой gzip он уходит как есть, остальным Varnish распаковывает на лету. И там же написано, что кеш обычно не упирается в процессор, так что жать разумнее ему, а не серверу приложений, где процессора чаще не хватает.

У nginx похожая схема собирается из двух частей. Модуль ngx_http_gunzip_module вошел в nginx 1.3.6 в сентябре 2012 года и распаковывает gzip-ответы для клиентов без поддержки gzip, а в документации написан сценарий, ради которого он нужен: хранить данные сжатыми, чтобы экономить место и ввод-вывод. Сжимать ответ перед записью в proxy_cache nginx не станет, он кладет в кеш то, что прислал upstream, и сжатие приходится поручать серверу за ним.

Это не баг, а вот такой дизайн, и спорили о нем еще в 2010 году. Максим Дунин (автор того самого фильтра) писал в рассылке nginx, что proxy_cache обязан складывать в кеш ровно то, что отдал upstream, ничего не трогая. Менять содержимое, по его словам, дело выходных фильтров. Ему тогда возражали: сам HTTP разрешает кешам трансформировать ответы, пока в заголовках нет Cache-Control: no-transform.

Новизна у Cloudflare тут в трех местах: zstd вместо gzip, сжатая копия, которая ездит между дата-центрами, и то, что дальше по конвейеру никто ничего не замечает. Последний пункт важнее всего. Сравните с Varnish: когда ему нужно распаковать объект для клиента без gzip, он сносит Content-Encoding и дописывает к ETag приставку W/. Вроде ерунда, а след остается, и кто-нибудь ниже по цепочке на него наткнется. У Cloudflare из кеша выходят те байты, что туда положили. Для всего, что стоит за ней, просто ничего не произошло.

Теперь о том, что и где у них сжимается.

На проводе сжато, на диске сыро

От origin до браузера ответ проходит два этапа, и жмутся они по-разному. Клиенту Cloudflare сжимает сама: смотрит на Accept-Encoding браузера и на тариф зоны. На бесплатном плане по умолчанию отдает Zstandard, на Pro и Business Brotli, на Enterprise gzip. К origin она ходит всегда одинаково, с Accept-Encoding: br, gzip. То есть сжатую версию просит, но origin вправе отказать, и тогда несжатый ответ как есть осядет в кеше.

Почему origin так часто отвечают без сжатия, в посте не объясняют. Но одна причина лично мне очевидна: дефолты. В nginx директива gzip по дефолту выключена. Включите ее — и сжиматься будет только text/html. JSON, CSS и JavaScript надо руками дописывать в gzip_types. А в конфиг сервера CDN не лезет, это дело владельца.

Снаружи это вообще никак не заметить. Браузеру прилетает сжатый ответ, в девтулзах content-encoding на месте — все, сайт типа оптимизирован, вопрос закрыт. Да и вообще, кто полезет в конфиг nginx, когда все работает?

При этом сами алгоритмы — штука очень древняя. gzip оформили как RFC 1952 аж в 1996 году. Brotli дождался своего RFC 7932 только в 2016-м, и тогда же Facebook выложила в опенсорс Zstandard. Целых 30 лет люди думали, как плотнее запихать текст в последнюю милю до браузера. При этом тот же zstd в Chrome завезли только в 123-й версии, март 2024-го, Firefox подтянулся в мае со 126-й, а Safari, как обычно — дотянул до 26.3 лишь в феврале 2026-го. И вот только тогда zstd как Content-Encoding стал Baseline.

А середину пути — от origin до CDN и внутри самой CDN за те же тридцать лет жать никто не додумался. Что origin прислал, то на диске и лежит, в исходном виде. И именно в этот забытый всеми кусок и лезет Cache Transcoding.

А чего вдруг сейчас

Память и диски за год резко подорожали. TrendForce насчитал по обычной DRAM рост контрактных цен на 93=98% за один только первый квартал 2026 года, то есть почти вдвое за три месяца.

С дисками тоже не лучше.

Для CDN это работает по главной метрике. Кеш-то конечный: забился — вытесняет старое, чтобы влезло новое. А каждый вытесненный объект, который потом снова запросят, оборачивается промахом. Бежим в вышестоящий дата-центр или вообще к origin, клиент ждет дольше, владелец сайта платит за исходящий трафик. Вариантов нарастить емкость по сути два: докупить дисков или уместить на тех же дисках больше объектов. Первый в 2026-м стал дорогим и медленным, второй упирается только в процессор.

Что вообще сжимают и по каким правилам

Сжимать все подряд никто не собирается: картинки, видео и шрифты уже сжаты собственными форматами, второй проход по ним сожжет процессор впустую. В выборке трафика этот медийный срез дал 21,4% запросов и 63,3% байтов, а у текста пропорция обратная — 67,3% запросов и всего 22,3% байтов.

Правила отбора: Cache Transcoding срабатывает, только если ответ 200 OK, Content-Encoding не выставлен, Content-Type — сжимаемый текст, а Content-Length известен и не меньше 4 КиБ. Мимо проходят запросы диапазонов, предсжатые ответы, бинарные данные и тела неизвестной длины — не зная размера заранее, прокси не может прикинуть, стоит ли овчинка выделки. Порог в 4 КиБ отсекает прорву мелких ответов, а теряется на этом около 1% байтов, которые иначе подошли бы под сжатие. И порог, и уровень zstd в посте названы параметрами, а не константами: уровень 3 и 4 КиБ взяты как точка отсчета, чтобы сначала измерить саму архитектуру.

Где это все живет

Прототип живет внутри Pingora. Это прокси-фреймворк на Rust, которым Cloudflare заменила у себя nginx. Рассказали о нем еще в 2022 году, а в феврале 2024-го выложили код под Apache 2.0. Стоит Pingora в сервисе, который соединяет сеть компании с origin-серверами. Место выбрали не от балды: через эту точку объект проходит ровно один раз за всю свою жизнь в кеше, дальше его только читают. Жмут поэтому там, а не на выходе к браузеру.

Дальше все довольно скучно. При промахе кеша прокси забирает тело у origin, жмет его zstd, пишет на диск и помечает в метаданных, что хранимая копия сжата. Исходную длину тоже сохраняет. Перед отдачей разворачивает обратно. При попадании еще короче: прочитали, распаковали, отдали.

Интереснее с Tiered Cache. Это функция, которая делит дата-центры на два яруса, и к origin ходит только верхний. Верхний ярус жмет объект один раз, хранит у себя zstd и вниз передает в том же сжатом виде. Нижний кладет на диск ту же копию и распаковывает ее только на последнем шаге, перед клиентом. Сжать дважды не получится: ярус, получивший объект от соседа, видит по маркеру, что тот уже в zstd. Работает это на всех тарифах, включая бесплатный.

Вторая половина выгоды тут даже интереснее первой. Место на диске экономится один раз на объект. А трафик между ярусами экономится каждый раз, когда нижний ярус заполняется из верхнего. Чем больше нижних дата-центров у зоны, тем чаще один и тот же объект едет вниз, и тем больше раз окупается единственное сжатие наверху.

Все, что стоит после кеша, получает объект как ни в чем не бывало: те же байты, та же длина. А на бесплатном тарифе, где посетителю по умолчанию уходит Zstandard, так, что текст сжали при записи на диск, распаковали при чтении и тут же сжали еще раз, тем же алгоритмом.

Сжал один раз, распаковывай всю жизнь

Цифры в посте тоже есть.

Сжатие — 4,31 нс на байт, около 232 МБ/с, и платится один раз, при записи объекта в кеш. Распаковка — 1,56 нс на байт, около 641 МБ/с, но платится при каждой отдаче. На файле в 272 КиБ из их набора выходит примерно 1,2 мс процессорного времени на сжатие и 0,43 мс на каждую распаковку.

При этом надо понимать, что те же несколько процентов дополнительной нагрузки на CPU на уровне zstd 3 — это не замер живой сети, а модель. Она верна при определенных допущениях о трафике и о том, сколько раз отдают объект. Поменяйте допущение, и цифра поедет.

Распаковка выходит почти втрое дешевле сжатия, и весь дальнейший счет стоит на этой пропорции. Диск освобождается сразу, а процессорные расходы растут с каждым запросом. Уже после третьей отдачи распаковка съедает больше, чем стоило сжатие.

Поэтому и завернули первую идею, которую команда обсуждала. Хотели жать только популярный контент, мол, его чаще переиспользуют, а значит, и толку больше. Но потом догадались, что горячие объекты ведь и распаковываются чаще всех. В итоге ограничение резало экономию места сильно, а нагрузку на процессор почти никак. Победил самый примитивный вариант: сжимать вообще весь подходящий текст начиная с 4 КиБ.

Из этой же пропорции, кстати, следует кое-что про планы поднять уровень zstd, которые Cloudflare держит первым пунктом в списке будущих работ. Распаковка у zstd почти не зависит от уровня сжатия — это свойство всего семейства LZ, и в документации оно прописано прямым текстом. Значит, подъем уровня утяжеляет только запись, а чтение остается ровно там, где было. Для схемы, которая платит за сжатие однократно, а распаковывает на каждой отдаче, расклад неожиданно удачный: дорожает ровно та половина счета, которая и так случается один раз за жизнь объекта. Так что третий уровень тут, похоже, и правда точка отсчета, а не потолок.

На Hacker News из той же арифметики сделали обратный вывод: жать логичнее холодное. Читают его редко, распаковка почти ничего не стоит, а места оно занимает столько же. Один комментатор описал это через иерархию хранения, где холодные данные всегда уезжают на более дешевый и медленный уровень, и сжатие здесь и есть такой уровень, только на том же диске. В том же треде ему возразил человек с опытом работы в крупном CDN: популярность там распределена крайне неравномерно, 80–90% трафика давали 10% объектов, а чуть ли не половину уникальных объектов повторно не запрашивали днями. Процессор, потраченный на сжатие такого объекта, не окупится уже никогда.

Cloudflare в итоге встала ровно посередине между этими позициями. Холодный хвост дает основную экономию места, горячая голова — основную нагрузку на процессор, и суммарные несколько процентов компанию устраивают. Объекты, которые больше никто не откроет, прототип все равно жмет, и деваться тут некуда: платят за сжатие при записи, а будет ли вторая отдача, заранее не знает никто.

Нюанс

Теперь про коэффициент 2,8, потому что получен он не на серьезном трафике. Гоняли больше миллиона запросов через 10 кеш-серверов, половину кампании с выключенным Tiered Cache, половину с включенным. А тестовых объектов было всего два, около 195 и 272 КиБ, и оба сжались примерно в 2,8 раза. Набор Cloudflare сама называет намеренно сжимаемым и константой для всей сети измеренный коэффициент считать не предлагает.

Хотя натянутой она не выглядит. В 2024 году, запуская Zstandard для браузеров, Cloudflare на сутки перевела трафик бесплатного тарифа с Brotli на zstd того же третьего уровня. Миллиарды запросов, от HTML до JavaScript, средний коэффициент 2,86. Два тестовых файла легли почти точно туда же.

С самими петабайтами сложнее, потому что доли запросов и байтов, которые приводит компания, описывают трафик, а не содержимое дисков. На диске оседает то, что дожило до второго запроса, и пропорции там свои. Сколько места текст занимает в кеше, Cloudflare не раскрывает, как не раскрывает и общий его объем. Перемножить проценты на емкость и получить петабайты своими силами не выйдет.

Цифр задержки тоже нет, а вопрос любопытный: распаковка добавляет работу на последнем переходе, зато с диска читается почти втрое меньше байтов. Что перевесит, непонятно, и в посте этого не мерили. Так что петабайты я бы читал как оценку масштаба по сжимаемому тестовому набору, а не как измеренный результат. Заголовок поста, кстати, и написан в сослагательном наклонении: «Как мы могли бы сэкономить».

Сослагательное наклонение спасает заголовок, а вот название прототипа в комментариях не приняли. Слово пришло из видео и аудио, где транскодирование это перевод из формата в формат, а тут всего лишь жмут при записи и распаковывают при чтении. Название станет честным, если сбудутся планы из финала поста: научиться работать с предсжатыми ответами origin и отдавать zstd дальше без распаковки. Тогда это и правда будет перекодирование между представлениями.

К тому же в комментариях вполне предсказуемо догадались до range-запросов. Пока объект лежит на диске несжатым, ответить на Range просто: прочитал нужный кусок файла, отдал. Со сжатым потоком этот фокус не пройдет. Смещение в сжатых байтах никак не связано со смещением в исходных, и чтобы добраться до нужного места, поток придется разворачивать с самого начала. Пост этого не объясняет, а отделывается фразой, что range-запросы прототип не трогает и их еще предстоит исследовать.

Хотя решение лежит на поверхности уже девять лет. В каталоге contrib репозитория zstd с 2017 года живет спецификация seekable format. Данные режутся на независимо сжатые кадры, таблица их размеров кладется в конец файла, и декодер по ней прыгает куда надо. Платить приходится чуть худшим сжатием и лишним индексом.

Почему за него не взялись, в посте не сказано. Подозреваю, дело в прописке: seekable format лежит в contrib, а не в основной спецификации zstd.

Со сжатыми ответами origin все иначе. Если сервер прислал gzip, прототип хранит его как есть. Хотя по замерам той же Cloudflare, zstd жмет примерно на 11% компактнее. Но платить за него придется полной распаковкой плюс полным сжатием вместо одного прохода. А вся экономика прототипа построена как раз на том, что проход один.

С Brotli арифметика и вовсе переворачивается: его средний коэффициент был 3,08 против 2,86 у zstd. Перекодирование в zstd третьего уровня здесь просто проиграет в объеме.

Сроков запуска в посте не называют. В планах — все то же самое, но на данных пошире: уровни zstd повыше, другие типы контента и размеры объектов.

Что проверить у себя

Владельцу сайта за Cloudflare от прототипа пока ни жарко ни холодно, браузер сжатое получает и так и этак. Но сам факт интересен: он показывает, где у большинства сайтов реально дыра. И дыра не там, куда все смотрят. Все пилят отдачу в браузер, а сырым оказывается передача от сервера в CDN.

Проверить свой origin — дело одной минуты. Шлете ему запрос напрямую, в обход CDN, с тем же Accept-Encoding: br, gzip, которым ходит Cloudflare, и смотрите, есть ли в ответе Content-Encoding. Нет заголовка? Вы в тех самых 71%.

Проверка занимает секунд тридцать, и находится обычно что-нибудь неприятное. Content-Encoding редко отваливается на сайте целиком, чаще на чем-то одном. Базовая картина: статику раздает nginx и жмет ее, а JSON прилетает от приложения напрямую, мимо всех настроек. Или наоборот, везде все настроено правильно, но полгода назад подняли сервис на поддомене, и про gzip_types там никто не вспомнил. Снаружи ни один из этих случаев не виден: CDN все заглаживает.

Что делать дальше — смотря что у вас дороже. Платите за исходящий трафик из облака или упираетесь в канал — жмите у себя: счет уменьшится, кеш станет заполняться быстрее. Cloudflare примет и gzip, и Brotli, причем Brotli на тексте плотнее того самого zstd третьего уровня, на котором работает прототип. А если в процессор упирается сам сервер приложений, сжатие разумнее отдать кеширующему слою.

Если кеширующий слой у вас свой, то все упирается в поведение самого кеша. Вытесняет текстовые объекты, за которыми потом возвращаются, — сжатие при записи выкупит часть этих промахов обратно и оправдает себя. Не вытесняется — тогда остаются экономия диска и трафика между узлами, а платить за них придется процессором на каждой отдаче.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.