Что страница на самом деле способна определить о headless-Chrome
Спросите десять человек, определяется ли headless-Chrome, и получите десять ответов, большинство из которых устарели. Честный ответ изменился, когда Chrome пересобрал headless на том же коде, что и полный браузер, и меняется ещё раз в зависимости от того, есть ли у машины видеокарта.
Что выдавал старый headless
Изначальный режим --headless работал по действительно другому пути кода, и это было видно:
- Токен в UA. Строка user agent прямо несла
HeadlessChrome/xx, и множество антибот-правил проверяли одну эту подстроку. navigator.pluginsиnavigator.mimeTypes. Headful-Chrome сообщает небольшой набор встроенных плагинов, включая просмотрщик PDF. Старый headless отдавал пустой массив, чего настоящий десктопный браузер не делает.- Странности окна и экрана.
outerHeightиouterWidthв старом headless возвращали0, потому что не было настоящей обвязки браузера, которую можно измерить. Значенияwindow.screenтоже могли выглядеть неправдоподобно - вьюпорт без соответствующего физического дисплея. - Поведение разрешений. Вызовы вроде
navigator.permissions.query({name: 'notifications'})иногда бросали исключение или разрешались вразрез с собственным состоянием Notification API, а такого противоречия настоящий браузер не создаёт.
Что починил новый headless
Chrome 112 добавил --headless=new, пересобранный на том же коде, что и полный браузер, а не на урезанном конвейере, а Chrome 132 убрал старую реализацию, так что обычный --headless теперь означает новый режим. Старый живёт отдельно как chrome-headless-shell. Строка UA больше не несёт токен HeadlessChrome по умолчанию, navigator.plugins сообщает тот же встроенный набор, что и обычное окно, а outerHeight/outerWidth ведут себя как положено. Большая часть списка выше, та самая, которую до сих пор повторяют по статьям времён до 112-й версии, больше не применима.
Признак, который действительно остался: нет видеокарты
Чего новый headless не починил, потому что это не проблема headless, - это то, что происходит на машине без графического железа.
Рендерер WebGL. Запросите WEBGL_debug_renderer_info и значение UNMASKED_RENDERER_WEBGL. На настоящем десктопе это будет что-то вроде строки ANGLE, называющей реальную видеокарту NVIDIA, AMD или Intel. На headless-сервере без видеокарты Chrome откатывается к программному растеризатору, SwiftShader на большинстве платформ или llvmpipe под Mesa на Linux, и строка так прямо и говорит. Настоящие пользовательские машины практически никогда не работают на программном рендерере WebGL, что делает этот признак одним из самых надёжных серверных.
Отсутствующие кодеки. В минимальных серверных образах часто нет библиотек декодирования H.264 или AAC, которые по умолчанию есть в десктопной ОС, поэтому проверки canPlayType и реальное воспроизведение могут отличаться от того, что сообщает пользовательский браузер.
Доступность шрифтов. На голом Linux-сервере обычно стоит горстка шрифтов, чаще всего DejaVu и Liberation, против трёхсот и более на настоящей установке Windows или Mac. Перечисление шрифтов через метрики текста на canvas - давно известный побочный канал, и он сильно коррелирует с headless-развёртываниями просто потому, что именно там живут минимальные наборы шрифтов.
Ничего из этого не вызвано самим флагом --headless. Всё это вызвано работой на железе и образе ОС, которые никогда не предназначались для отрисовки дисплея, а headless-автоматизация часто именно там и живёт.
Практический вывод
Если вы работаете на машине с настоящим дисплеем, десктопе или ноутбуке, работайте в headful. Для большинства задач это не стоит ничего значимого и полностью снимает вопрос видеокарты, потому что у вас уже есть настоящий графический драйвер и настоящий оконный менеджер, дающие настоящую строку WebGL.
Если работать приходится на сервере, решать надо именно историю с видеокартой, а не флаг headless. Виртуальный дисплей и, где возможно, реальный или виртуализованный путь к видеокарте значат больше любой заплатки на UA или плагины, потому что строку рендерера скрипт не может легко подделать, не подделав заодно и саму отрисовку за ней.
Когда бесплатный подход - ответ лучше
Если у вашей задачи вообще нет вопросов с детектом, внутреннее тестирование, скрапинг собственного стенда, генерация PDF, обычные Playwright или Puppeteer в headless по умолчанию бесплатны, хорошо поддерживаются и уже несут описанные выше исправления нового headless. Антидетект-браузер не нужен, чтобы закрывать признаки, которые Chromium закрыл сам.
Где мы честны о своих ограничениях
Опция headless в AntiBrow на macOS сейчас ничего не делает: включение всё равно открывает полноценное окно, потому что эквивалента для macOS пока не реализовано. На Linux headless-отрисовка требует Xvfb, виртуального дисплейного сервера, поскольку браузеру всё равно нужно где-то рисовать, даже если никто не смотрит. Ни то, ни другое не маскируется, потому что читатель выяснит и то и другое за один прогон.
Проверьте сами
Откройте страницу и прочитайте navigator.plugins.length, navigator.userAgent и значение UNMASKED_RENDERER_WEBGL из контекста WEBGL_debug_renderer_info. На новом headless без видеокарты плагины и UA будут выглядеть нормально, а строка рендерера всё равно скажет SwiftShader или llvmpipe. Основную работу сейчас делает именно это поле.
Наш чек-лист согласованности отпечатка разбирает проверки согласованности вокруг этого, включая то, как строка рендерера должна сходиться с заявленной ОС и видеокартой.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.