surf
- title
- surf
- type
- toolbox
- summary
- HTTP-клиент на Go, маскирующийся под Chrome и Firefox вплоть до TLS-рукопожатия и пакетов QUIC
- tags
- go, http-client, tls-fingerprinting, browser-impersonation, scraping, quic, watchlist
- language
- Go
- license
- MIT
- created
- 2026-05-06
- updated
- 2026-07-29
- lang
- ru
- translation_of
- surf
- source_updated
- 2026-07-29
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Библиотека HTTP-клиента на Go, которая имитирует настоящие браузеры вплоть до TLS-рукопожатия и начального пакета QUIC. Нацелена на ту же нишу, что и curl-impersonate и tls-client: веб-автоматизация и скрейпинг сайтов, снимающих фингерпринты клиентов (Cloudflare, Akamai, PerimeterX, DataDome). Имитирует Chrome v145 и Firefox v148 на Windows, macOS, Linux, Android и iOS, с фреймами HTTP/2 SETTINGS, сохранением порядка заголовков и полноценным HTTP/3 поверх QUIC.
Что делает
Стандартный net/http.Client легко выдаёт себя фингерпринтом: его TLS ClientHello, HTTP/2 SETTINGS и порядок заголовков неизменны и не совпадают ни с одним реальным браузером. Системы защиты от ботов вычисляют хеши JA3/JA4 по TLS-рукопожатию и HTTP/2-фингерпринты по содержимому фреймов, поэтому трафик Go сразу выделяется. SURF заменяет транспорт на такой, который выдаёт побайтово эквивалентные рукопожатия для выбранной комбинации версии браузера и ОС, включая вариант QUIC для сайтов только с HTTP/3.
Библиотека также оборачивает жизненный цикл запроса в fluent builder API и модель ошибок на основе типа Result (g.Result[T]), взятую из enetx/g, поэтому работа с ней совсем не похожа на стандартный net/http.
Как устроен
TLS-фингерпринтинг опирается на uTLS (refraction-networking/utls) - стандартный форк для Go, предоставляющий доступ к спецификациям ClientHello. SURF выбирает HelloID или полную HelloSpec под каждую комбинацию браузера и ОС. Фингерпринты JA3/JA4 также можно задавать явно или рандомизировать.
QUIC/HTTP/3 использует quic-go с параметрами, подогнанными под браузер: порядок заголовков, порядок фреймов, значения SETTINGS и фингерпринт JA4QUIC, сформированный из начального пакета (Initial Packet) и TLS ClientHello. Поддерживаются SOCKS5 UDP-прокси, благодаря чему HTTP/3 остаётся доступен и за прокси; при использовании HTTP-прокси происходит автоматический откат к HTTP/2.
HTTP/2 настраивается на уровне фреймов SETTINGS: размер таблицы заголовков, включение push, начальный размер окна, максимальное число параллельных потоков и так далее - всё это задаётся в пресетах браузеров в точности по реальным значениям, отправляемым Chrome и Firefox.
Порядок заголовков сохраняется от начала до конца. Большинство HTTP-библиотек для Go нормализуют порядок заголовков через http.Header из стандартной библиотеки (который представляет собой map); SURF использует форк enetx/http, сохраняющий исходный порядок, поскольку антибот-системы учитывают последовательность заголовков как часть фингерпринта.
Builder API выстраивает цепочку конфигурации: surf.NewClient().Builder().Impersonate().Chrome().JA().Randomized().HTTP2Settings()...Build(). Middleware подключаются через .With(fn, priority...) как хуки на уровне запроса, ответа или всего клиента.
Выход в Std() преобразует настроенный клиент SURF обратно в *net/http.Client для использования со сторонними библиотеками, требующими типы из стандартной библиотеки. Большинство возможностей маскировки фингерпринта при этом сохраняются; логика повторных попыток (retry), кэширование ответов, отслеживание удалённого адреса и замеры времени - нет.
Базовый стек: enetx/http (форк стандартного пакета HTTP), quic-go, uTLS, enetx/g (типы Result/Option и утилиты для строк).
Применение
Базовая имитация Chrome:
client := surf.NewClient().
Builder().
Impersonate().Chrome().
Build().Unwrap()
resp := client.Get("https://example.com").Do()
if resp.IsOk() {
fmt.Println(resp.Ok().Body.String().Unwrap())
}
Chrome под iOS с принудительным HTTP/3 и прокси SOCKS5 UDP:
client := surf.NewClient().
Builder().
Impersonate().IOS().Chrome().
Proxy("socks5://127.0.0.1:1080").
ForceHTTP3().
Build().Unwrap()
Сессия с хранилищем cookie, повторными попытками и кэшем тела ответа:
client := surf.NewClient().
Builder().
Impersonate().Firefox().
Session().
Retry(3, 2*time.Second).
CacheBody().
Build().Unwrap()
Загрузка multipart со смешанными источниками:
mp := surf.NewMultipart().
Field("description", "Files").
File("document", g.NewFile("/path/doc.pdf")).
FileBytes("data", "data.json", g.Bytes(`{"k":"v"}`))
resp := client.Post("https://api.example.com/upload").Multipart(mp).Do()
Ограничения
- Требуется Go 1.25+.
- Целями для имитации служат Chrome v145 и Firefox v148; поддержка актуальности при выходе новых версий браузеров целиком лежит на мейнтейнере.
- API с типом Result (
g.Result[T]из enetx/g) не идиоматичен для Go и затягивает нетривиальную зависимость с generic-утилитами. Для интеграции в существующие кодовые базы потребуются adapter-shim'ы. - Проект одного автора (Enetx). Активно развивается, но bus factor равен 1.
- При переходе через
Std()теряются повторные попытки, кэширование тела ответа, удалённый адрес и тайминги. - TLS-фингерпринтинг через uTLS - постоянно ускользающая цель: разработчики защит от ботов регулярно обновляют механизмы детекта, и работающая маскировка сегодня не гарантирует успеха в следующем квартале.
Связи
Относится к той же сфере обхода фингерпринтинга, что и прокси-ядро в sing-box, руководства по VLESS-кластерам в xray-tutorial и более широкий инструментарий в russia-vpn-bypass-state-2026-04 - хотя те работают на уровне транспортных протоколов, тогда как SURF действует на прикладном уровне HTTP. Полезен для обвязок агентов, создающих скрейперы; по части оркестрации см. sandboxing-ai-agents.
Репозиторий
github.com/enetx/surf - Go, MIT, 1.7k звёзд (88 форков). Один автор, активная разработка.
Внесён в watchlist из-за bus factor 1 на фоне постоянной гонки версий браузеров: фингерпринты необходимо непрерывно обновлять вслед за релизами Chrome и Firefox.