Смена мобильного IP каждые две минуты не делает клиента новым для антибота. Разбираю четыре слоя, по которым его узнают за прокси (TCP/IP, TLS, HTTP/2, поведение), и показываю на суточном логе из 2 272 207 смен IP на 9 067 модемах, почему сама ротация по таймеру превращается в поведенческий отпечаток. Плюс честный список того, что в этом разборе не получилось.

С чего началось
Один из самых частых вопросов к поддержке мобильных прокси звучит примерно так. Стоит ротация раз в две минуты, IP каждый раз новый, а сайт всё равно узнаёт клиента и режет ему выдачу или подсовывает капчу. Отвечать «дело в отпечатке браузера» можно, но это ответ ни о чём.
Я работаю инженером в mobileproxy.space и решил ответить подробнее. Клиента за мобильным прокси можно узнать по четырём слоям, и смена IP не трогает ни один из них. Три слоя ниже сетевого адреса я разберу как механику, без цифр, потому что стенда для замеров у нас пока нет. Четвёртый, поведенческий, посмотрю на реальной телеметрии нашей сети за сутки. И главный сюрприз в том, что сама ротация по таймеру и есть поведенческий отпечаток.
Половина читателей этого хаба ловит ботов, а не запускает их, поэтому пишу так, чтобы можно было поймать меня за руку на любой цифре.
Откуда цифры
Единственный источник чисел в статье это внутренний лог, куда пишется каждая смена IP на каждом модеме сети. Окно разбора с 21.09.2026 12:00 по 22.09.2026 11:52 МСК, всё посчитано агрегатами по этому логу, без выборочных примеров «на глаз».
За эти сутки в логе 2 272 207 смен IP на 9 067 модемах и 7 712 прокси‑портах. Смен по таймеру 1 195 083 (52,6%), по ссылке или API 1 077 124 (47,4%). Активных модемов в любой час суток было от 5 553 до 6 223.
Интервалы между сменами одного модема на полном логе считать было долго, поэтому взял выборку 10% модемов по остатку от деления идентификатора. Получилось 113 785 интервалов по таймеру и 98 089 по ссылке. Запрос интервалов (оконная LAG по каждому модему; имена таблицы и полей здесь условные; возврат того же IP считался тем же приёмом через LAG по адресу):
SELECT who, CASE WHEN gap < 60 THEN 'a <60' WHEN gap < 120 THEN 'b 60-119' WHEN gap < 300 THEN 'c 120-299' WHEN gap < 600 THEN 'd 300-599' WHEN gap < 1800 THEN 'e 600-1799' ELSE 'f 1800+' END AS bucket, COUNT(*) AS n FROM ( SELECT who, CAST(ts AS SIGNED) - CAST(LAG(ts) OVER (PARTITION BY modem_id ORDER BY ts, id) AS SIGNED) AS gap FROM ip_change_log WHERE modem_id MOD 10 = 0 ) t WHERE gap IS NOT NULL GROUP BY who, bucket;
На этом же запросе я нашёл дефект нашего логгера, но о нём ниже, в разделе про то, что пошло не так.
Где заканчивается каждый слой
Чтобы дальше было понятно, кто что видит, нужно один раз проследить путь запроса.

Клиент подключается к порту на нашей ноде. У каждого порта два входа, HTTP и SOCKS5, причём SOCKS всегда на единицу больше HTTP. Это, пожалуй, единственное соглашение в сети, которое ни разу не обсуждалось на созвонах, его просто все знают. Прокси‑демон принимает соединение, а агент ноды управляет модемами, в том числе отдаёт команду на смену IP. Дальше демон открывает новое TCP‑соединение через модем, трафик проходит NAT оператора и приходит на целевой сервер.
TCP‑сессия клиента заканчивается на ноде. TLS‑сессия проходит через ноду насквозь, потому что для HTTPS прокси работает как туннель CONNECT. HTTP/2 живёт внутри TLS и тоже проходит насквозь. Поведение целиком на стороне клиента и его скриптов.
Смена IP меняет только адрес после NAT оператора.
Слой 1. TCP/IP
Первое, что видит сервер, ещё до всякого TLS, это SYN‑пакет. В нём есть стартовый TTL, размер окна, MSS, набор и порядок TCP‑опций, масштабирование окна, SACK, таймстампы. Каждая операционная система и даже каждая крупная версия ядра выставляет их по‑своему, и на этом уже много лет работает пассивное определение ОС, тот же p0f.
Через мобильный прокси целевой сервер видит не стек клиента, а стек нашей ноды, потому что именно она открывает соединение к серверу. С точки зрения маскировки это плюс, ОС клиента наружу не торчит. С точки зрения связывания сессий это минус. Стек ноды один и тот же при каждой смене IP, и все адреса, которые модем получил за день, приходят на сервер с одинаковым TCP‑профилем.
Одного TCP‑профиля для опознания мало, он общий у огромного числа машин на такой же ОС. Но как признак «это точно не смартфон» он работает. Мобильный IP из пула оператора и серверный стек в одном SYN это несовпадение, которое антибот вполне может учесть.
Что делает с TTL и опциями NAT оператора, я не проверял. Предположительно адреса и порты переписываются, а опции проходят нетронутыми, но это как раз то, ради чего нужен стенд.
Слой 2. TLS, JA3 и JA4
ClientHello прокси не трогает вообще. Сервер получает его ровно таким, каким его собрала TLS‑библиотека клиента. Из него считается JA3, хеш от версии протокола, списка шифров, списка расширений, эллиптических кривых и форматов точек. JA4 добавляет к этому ALPN, признак SNI, количество шифров и расширений и сортирует списки, чтобы хеш не ломался от рандомизации порядка расширений в свежих браузерах.
Одна и та же сборка Chrome даёт одинаковый JA4 на любом IP. Тот же curl или requests на Python дают свой, узнаваемый и совершенно не браузерный отпечаток. Если один хеш прилетает с мобильного адреса, потом с другого адреса того же оператора, потом с третьего, а между запросами пауза строго две минуты, связать эти сессии несложно.
Есть и более прямой механизм. TLS позволяет возобновить сессию по тикету или PSK. Клиент, который после смены IP предъявляет тикет от предыдущего соединения, сам сообщает серверу, что он тот же самый. Никакой эвристики не нужно.
Отключают ли возобновление наши клиенты, я не знаю. Данных о содержимом их TLS у нас нет и быть не должно.
Слой 3. HTTP/2
Внутри TLS живёт ещё один набор параметров, который клиент выставляет при открытии HTTP/2-соединения. Первый кадр SETTINGS с размером таблицы заголовков, лимитом потоков и начальным окном. Кадр WINDOW_UPDATE. Кадры PRIORITY, которые часть браузеров шлёт при старте, а часть нет. Порядок псевдозаголовков в первом запросе, у разных браузеров разный, у HTTP‑библиотек тоже. Из этого собирается стабильный отпечаток, и он, как и JA4, не зависит от IP.
Сюда же честно нужно отнести и совсем скучное. Cookie, localStorage, ETag в кеше, идентификатор в теле запроса. Клиент, который меняет IP каждые две минуты и ходит с одной и той же сессионной cookie, тратит смены зря. Сколько таких среди наших пользователей, я оценить не могу, прокси не видит содержимое HTTPS.
Три слоя, и ни один из них не знает, что IP поменялся.
Слой 4. Поведение, на этот раз с цифрами
Здесь у меня есть данные, и они оказались интереснее, чем я ждал.
Гребёнка ровных минут
Смены по таймеру распределены по интервалам так. p10 и p50 по 120 с, p90 равен 420 с, p99 равен 1 199 с. Из 113 785 интервалов 102 231, то есть 89,8%, кратны ровно минуте. Пики с допуском плюс‑минус две секунды:
Интервал |
Доля интервалов по таймеру |
120 с |
55,1% |
180 с |
16,3% |
300 с |
14,7% |
600 с |
5,0% |
900 с |
2,2% |
Больше половины всех таймерных ротаций в сети идут ровно раз в две минуты.

Смены по ссылке ведут себя иначе. Если выбросить интервалы короче 30 с (почему, объясню в разделе про дефекты), получается p10 = 47 с, p50 = 283 с, p90 = 1 305 с, p99 = 5 140 с. Кратных минуте среди них около 0,8%. Но и здесь есть свой пик, 305 с, на него приходится 15,6% записей. Это клиентские скрипты с циклом «спать пять минут, дёрнуть ссылку», где к 300 с добавляется время самой смены. Второй кластер сидит около 1 310–1 325 с, та же история.
Один реальный порт с таймером. За два часа 60 смен, все интервалы от 119 до 121 с. Все 60 адресов разные, из трёх сетей /16 одного оператора. Каждая смена заняла от 2,2 до 19,3 с.
Теперь поставьте себя на место антибота, у которого уже есть JA4 этого клиента. Он видит поток запросов с одинаковым отпечатком, в котором каждые две минуты адрес меняется на соседний из того же пула, а перед сменой запросы на несколько секунд пропадают. На человека с телефоном это не похоже. Это похоже на cron.
Порт со сменой по ссылке из того же лога выглядел иначе. 21 запись за три часа, из них 6 дублей, а реальные интервалы разбросаны от 115 до 1 838 с. Гребёнки нет.
Новый IP не всегда новый
После смены по таймеру новый адрес совпал с предыдущим в 1,27% случаев (1 401 из 110 728), попал в пять предыдущих в 1,97%, в десять предыдущих в 2,29%. После смены по ссылке с интервалом от 30 с уже 8,0%, 9,2% и 9,7% (4 266, 4 900 и 5 165 из 53 302).
А вот если между двумя сменами по ссылке прошло меньше 30 с, в 68,1% случаев (27 674 из 40 658) вернулся тот же IP. Модем ещё не переключился, повторный запрос забрал старый адрес. Практический вывод для тех, кто дёргает API из скрипта. Не проверяйте IP сразу после команды, дождитесь ответа о завершении.
За сутки медианный модем сделал 97 смен и получил 89 уникальных адресов, p90 равен 713 сменам и 585 адресам, максимум 8 534 смены и 6 127 адресов. Доля смен, давших ещё не виденный за сутки этим модемом адрес, у тихих модемов 67%, у активных 80–87%.
И отдельная группа. 562 модема сделали в среднем по 45 смен и весь день получали один и тот же адрес. Липкий NAT оператора, смена проходит, адрес снаружи не меняется. Как это выглядит с той стороны, понятно и без графика.

Сколько адресов проходит через один порт за час
Считал по прокси‑часам, всего их 139 105 на 7 712 портах.
Уникальных IP за час на порт |
Прокси‑часов |
Доля |
1 |
10 843 |
7,8% |
2 |
11 342 |
8,2% |
3–5 |
26 631 |
19,1% |
6–10 |
22 371 |
16,1% |
11–20 |
39 065 |
28,1% |
21–30 |
25 022 |
18,0% |
31–60 |
2 353 |
1,7% |
60 и больше |
1 478 |
1,1% (в среднем 150,6 адреса в час) |
Самая частая картина, 11–20 адресов в час на порт, это как раз таймер на две‑пять минут. 1,7% прокси‑часов с 31–60 адресами и 1,1% с 60 и больше это смена чаще, чем раз в минуту. Что человек хочет получить, меняя мобильный IP чаще, чем каждые полминуты, я честно не понимаю. С точки зрения слоёв 1–3 он просто раздаёт свой отпечаток по полторы сотни адресов за час.

Пул оператора
Здесь самое неприятное. Наш парк за сутки увидел вот такую часть адресного пространства операторов.
Оператор |
Модемов |
Смен |
Уникальных IP |
Сетей /24 |
Сетей /16 |
Уник. IP от смен |
Средняя смена |
Билайн |
3 017 |
700 365 |
104 867 |
605 |
26 |
15,0% |
4,80 с |
МегаФон |
2 479 |
686 221 |
88 884 |
652 |
26 |
13,0% |
4,48 с |
Tele2 |
740 |
217 872 |
73 613 |
865 |
21 |
33,8% |
4,06 с |
Yota |
713 |
170 170 |
30 636 |
586 |
23 |
18,0% |
4,75 с |
МТС |
494 |
111 685 |
34 016 |
588 |
12 |
30,5% |
3,81 с |
Kyivstar |
301 |
92 253 |
46 362 |
1 087 |
8 |
50,3% |
3,97 с |
Vodafone UA |
179 |
69 671 |
44 311 |
633 |
7 |
63,6% |
5,89 с |
life UA |
176 |
62 349 |
26 889 |
183 |
4 |
43,1% |
4,27 с |
Предпоследний столбец читается так. У Билайна и МегаФона семь смен из восьми возвращают адрес, который за эти сутки уже получал кто‑то из наших модемов. У Kyivstar каждая вторая смена даёт адрес, которого сегодня ещё не было. При этом весь дневной трафик Билайна с 3 017 модемов уложился в 605 сетей /24 и 26 сетей /16.
Для антибота это означает, что «новый» мобильный IP с высокой вероятностью уже вчера или час назад приходил с тем же JA4 и тем же ритмом. Репутация копится на /24, и 605 сетей это совсем не тот объём, который сложно запомнить.

Ритм суток
Смены по таймеру идут ровно, от 48 459 до 50 895 в час, круглые сутки без провалов. Ручные и API‑смены растут с 29 782 в 5:00 МСК до 56 467 в 10:00, в 1,9 раза. У людей есть рабочий день. У cron его нет, и это видно даже на агрегате по всей сети, что уж говорить про отдельный клиентский отпечаток.

Пауза на смену
Сама смена, от команды модему до появления нового адреса, по таймеру занимает в среднем 4,59 с (минимум 0,55 с, максимум 31,95 с). Быстрее 3 с уложилось 42,5% смен, быстрее 5 с 75,2%, быстрее 10 с 92,8%, быстрее 20 с 99,4%. По ссылке в среднем 5,07 с и хвост длиннее, максимум 54,89 с. Быстрее 3 с там 37,8%, быстрее 5 с 67,7%, быстрее 10 с 90,6%.
По операторам разброс небольшой, от 3,81 с у МТС до 5,89 с у Vodafone UA. Почему у Vodafone UA дольше всех, я не разбирался, возможно, дело в конкретных нодах, где стоят эти 179 модемов.
Для отпечатка важно другое. Перед каждой сменой у клиента образуется пауза в несколько секунд, и на таймере она повторяется с той же периодичностью, что и сама смена. Ещё один зубец на той же гребёнке.

Что не получилось
Дефект логгера. 25,9% записей о сменах по ссылке (25 419 из 98 089 в выборке) оказались точными дублями, интервал 0–2 с, тот же IP, та же длительность. Одно событие писалось в лог два или три раза. Мы про это не знали, пока я не построил гистограмму интервалов и не увидел столб в нуле. Все цифры по сменам по ссылке в статье посчитаны либо без интервалов короче 30 с, либо с явной оговоркой. Где именно теряется идемпотентность записи, ещё предстоит найти, разбор уже идёт.
Нет стенда. Всё про TCP/IP, JA3/JA4, HTTP/2 и возобновление TLS выше это описание механики. Стенд с захватом трафика на выходе из модема и на принимающем сервере ещё предстоит собрать, и я не хочу писать «мы проверили», пока его нет.
Разбор только за сутки. Одно окно в 24 часа означает, что я не вижу недельных циклов, выходных и того, как пул оператора дрейфует со временем. Длинный ряд — отдельная работа с отдельной методологией.
Поле оператора. В таблице модемов примерно у 8 тысяч записей оператор не заполнен, такие модемы в операторскую таблицу не вошли. Сколько из них было активно именно в эти сутки, я не сверял, так что таблица по операторам это срез по тем, у кого поле заполнено, и ничего больше.
Выборка 10% по остатку от деления идентификатора. Смещения по операторам или нодам я не ожидаю, но и не проверял.
Что с этим делать
Обходить антибот я не обещаю, и список ниже не про это. Он про то, чтобы не платить за смены IP, которые ничего не дают.
Смена IP не сбрасывает ни TCP‑профиль ноды, ни ваш JA4, ни параметры HTTP/2, ни cookie и TLS‑тикеты. Всё, что выше сетевого адреса, находится на вашей стороне, и решать это надо там.
Таймер с ровным периодом сам по себе сигнал. Если ротация нужна, привязывайте её к событию (закончилась задача, закрылась сессия) или хотя бы добавляйте случайный сдвиг. Пик 305 с у скриптов показывает, что даже «ручной» цикл легко превращается в такой же таймер.
Не запрашивайте IP сразу после команды. При интервале меньше 30 с в 68,1% случаев вернётся старый.
Смена чаще раза в минуту размазывает один отпечаток по сотне адресов и не даёт ничего взамен. 1,7% плюс 1,1% прокси‑часов делают именно это.
Считайте репутацию по /24 и по оператору. У крупных российских операторов пул, который мы видим за сутки, невелик, и «новый» адрес чаще всего уже был.
Тем, кто на стороне защиты, всё это наверняка знакомо, но сформулирую как признак: одинаковый TLS‑отпечаток, пауза в несколько секунд каждые ровно 120 или 300 секунд и смена адреса внутри одного пула оператора сразу после паузы.
А для меня самый неожиданный итог разбора вообще не про антиботы. Четверть записей в одном из наших логов были дублями, и заметил это никто не мониторинг, а гистограмма для статьи. Теперь два открытых вопроса. Где именно агент ноды пишет событие дважды, и что на самом деле делает CGNAT оператора с TCP‑опциями на выходе из модема. На второй без стенда я отвечать не буду.
Автор работает в mobileproxy.space, телеметрия в статье снята с внутреннего лога смен IP сервиса.
Coprolog
А как же канвас? Если канваса нет, там и без смены айпишника понятно, что ботяра