Reddit через IPv6 прокси: почему нестабильно и как работать через apex-запись
Содержание
- Почему Reddit ломается на IPv6-прокси
- Анатомия DNS: A, AAAA и что видит прокси
- Apex-запись как точка входа
- Ротация IPv6 и почему Reddit её не любит
- HTTP/3 и почему он всё портит
- Cookies, CSRF и сессия
- Кейс: 403 на каждый второй запрос
- Кейс: OAuth-токен умирает через 5 минут
- Кейс: капча на каждом посте
- Сравнение подходов
- Что делать с apex на практике
- Мониторинг и метрики
- Итог
Почему Reddit ломается на IPv6-прокси
Reddit — один из самых проблемных сайтов для проксирования. Не потому что сложный, а потому что у него зоопарк поддоменов и агрессивная антифрод-система. Когда ты гонишь трафик через IPv6-прокси, первое, с чем столкнёшься — часть запросов уходит напрямую, минуя прокси. Браузер резолвит `www.reddit.com` в IPv6-адрес, а твой прокси настроен только на IPv4. Или наоборот.
Классика: `curl -6 https://www.reddit.com` работает, а `curl -4` висит. Или сессия живёт 30 секунд и отваливается с 403. Причина в том, что Reddit использует несколько CDN и балансировщиков, и часть из них отдаёт AAAA-записи, часть — только A. Прокси, который не умеет в happy eyeballs, будет упорно долбиться в недоступный стек.
Ещё хуже с OAuth и API. Reddit API (`oauth.reddit.com`) требует стабильный IP на протяжении всей сессии токена. Если прокси ротирует IPv6-адрес каждые 60 секунд, refresh token инвалидируется. Получаешь `401 Unauthorized` на ровном месте.
Анатомия DNS: A, AAAA и что видит прокси
Когда клиент делает запрос к `reddit.com`, происходит цепочка:
1. Резолв A/AAAA записи
2. Выбор адреса (RFC 6724 — правило выбора адреса)
3. TCP-соединение
4. TLS handshake (SNI = reddit.com)
5. HTTP/2 или HTTP/3 запрос
На каждом шаге IPv6 может сломаться. Пример вывода `dig`:
```
\$ dig AAAA www.reddit.com +short
2a04:4e42::396
2a04:4e42:200::396
2a04:4e42:400::396
2a04:4e42:600::396
\$ dig A www.reddit.com +short
151.101.1.140
151.101.65.140
151.101.129.140
151.101.193.140
```
Это Fastly. Один и тот же CDN, но адреса разные. Если твой прокси даёт IPv6-выход только через один upstream (скажем, Hurricane Electric), а Fastly ожидает трафик из конкретного ASN — получишь challenge или throttle.
Apex-запись как точка входа
Apex-домен (`reddit.com` без `www`) — это то, что пользователь набирает руками. И это ключевая точка. Apex обычно резолвится в те же IP, что и `www`, но есть нюанс: `reddit.com` отдаёт 301-редирект на `www.reddit.com` для большинства путей, кроме `/r/*` и API. Редирект — это ещё один запрос, ещё один TLS handshake, ещё одна возможность утечь реальным IP.
Схема через apex:
```
client -> proxy (IPv6) -> reddit.com (apex) -> 301 -> www.reddit.com
```
Если прокси не переписывает Location-заголовок, клиент может пойти напрямую на `www` по своему реальному IP. Вот тебе и деанон.
Правильная настройка — принудительный резолв apex в проксируемый адрес. В nginx:
```nginx
resolver 2a00:1450:4001:81b::200e valid=300s ipv6=on;
resolver_timeout 5s;
server {
listen [::]:443 ssl http2;
server_name reddit.com www.reddit.com;
location / {
proxy_pass https://\$upstream_reddit;
proxy_ssl_server_name on;
proxy_set_header Host www.reddit.com;
proxy_set_header X-Forwarded-For \$remote_addr;
proxy_redirect https://www.reddit.com/ https://reddit.com/;
}
}
```
`proxy_redirect` здесь критичен. Без него Location-заголовок укажет на настоящий `www.reddit.com`, и браузер попробует достучаться туда без прокси.
Ротация IPv6 и почему Reddit её не любит
У IPv6-прокси есть суперсила — огромный пул адресов. `/64` подсеть даёт 2^64 адресов. Можно ротировать хоть каждый запрос. С Reddit это не работает.
Reddit привязывает сессию к комбинации: IP + User-Agent + TLS fingerprint + cookies. Смена IP в середине сессии — красный флаг. Получаешь капчу, потом 429, потом бан подсети целиком.
Практика: держи один IPv6 на сессию минимум 10-15 минут. Для API — на весь срок жизни токена (обычно 1 час).
Пример на Python с `requests` и `requests-toolbelt`:
```python
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
session.proxies = {
"http": "http://user:pass@[2a01:4f8:c17:1234::1]:8080",
"https": "http://user:pass@[2a01:4f8:c17:1234::1]:8080",
}
retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 502, 503])
adapter = HTTPAdapter(max_retries=retry, pool_connections=10, pool_maxsize=10)
session.mount("https://", adapter)
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
}
r = session.get("https://www.reddit.com/r/python.json", headers=headers, timeout=15)
print(r.status_code, len(r.content))
```
Обрати внимание на скобки вокруг IPv6-адреса в URL прокси. Без них `requests` парсит адрес неправильно и падает с `InvalidURL`. Это грабли, на которые наступают все.
HTTP/3 и почему он всё портит
Reddit поддерживает HTTP/3 через QUIC. QUIC работает поверх UDP. Многие IPv6-прокси умеют только TCP. Клиент видит `Alt-Svc: h3=":443"` в заголовках и пытается перейти на QUIC — напрямую, минуя прокси. Утечка.
Решение: вырезать заголовок `Alt-Svc` на прокси.
```nginx
proxy_hide_header Alt-Svc;
proxy_hide_header Alternate-Protocol;
```
Или явно отдавать клиенту `Alt-Svc: clear`. Проверить, что утечки нет:
```bash
curl -6 -sI https://www.reddit.com/ | grep -i alt-svc
```
Если видишь `h3` — прокси не режет заголовок.
Cookies, CSRF и сессия
Reddit ставит несколько ключевых cookie: `reddit_session`, `token_v2`, `csrf_token`. Все они привязаны к IP. `reddit_session` живёт 1 час, `token_v2` — до года, но инвалидируется при смене IP.
Если ты используешь apex-запись и редирект, cookie выставляются на домене `.reddit.com`. Это ок. Но если прокси переписывает домен на что-то другое — cookie теряются.
Проверка цепочки:
```bash
curl -6 -v -c cookies.txt -b cookies.txt \
--proxy http://[2a01:4f8:c17:1234::1]:8080 \
https://reddit.com/r/linux 2>&1 | grep -E "Set-Cookie|Location"
```
Должен увидеть `301` с `Location: https://www.reddit.com/r/linux/` и `Set-Cookie` для `.reddit.com`.
Кейс: 403 на каждый второй запрос
Сервер на nginx 1.24, прокси через IPv6-пул Hetzner. Настройка стандартная: `proxy_pass` на apex, `proxy_set_header Host reddit.com`. Каждый второй запрос возвращает 403 с телом `blocked`.
Причина оказалась в SNI. nginx по умолчанию отправлял SNI = `reddit.com` (apex), а Fastly ожидал `www.reddit.com` для большинства путей. Плюс `proxy_ssl_name` не был задан, и nginx использовал `\$proxy_host` — то есть IP из `proxy_pass`.
Фикс:
```nginx
proxy_ssl_name www.reddit.com;
proxy_ssl_server_name on;
proxy_set_header Host www.reddit.com;
```
После этого 403 пропали. Задержка выросла с 180 мс до 220 мс — цена дополнительного TLS handshake к правильному SNI.
Кейс: OAuth-токен умирает через 5 минут
Скрипт на Python, `praw` через IPv6-прокси. Токен получается нормально, работает 3-5 минут, потом `401`. Перезапрос токена — та же история.
Причина: прокси-провайдер ротировал IPv6 каждые 60 секунд (sticky session не был настроен). Reddit привязывает OAuth-токен к IP, при смене — инвалидация. Причём `praw` не логирует смену IP, поэтому выглядит как случайные обрывы.
Решение — sticky session на стороне прокси. В конфиге пула:
```
session_ttl = 3600
sticky_by = client_ip
```
Плюс в коде — явный контроль IP через проверку `https://api.ipify.org` перед каждым запросом:
```python
import praw
reddit = praw.Reddit(
client_id="...",
client_secret="...",
user_agent="script:v1.0 (by /u/username)",
proxy="http://user:pass@[2a01:4f8:c17:1234::1]:8080",
)
me = reddit.user.me()
print(me.name)
```
`praw` поддерживает прокси через переменные окружения `HTTPS_PROXY`. Формат с IPv6 — обязательно в квадратных скобках.
Кейс: капча на каждом посте
Бот постит комментарии через apex-запись. Первые 5 проходят, дальше — капча. Смена IPv6 не помогает, капча возвращается через 2-3 поста.
Разбор показал: TLS fingerprint. `requests` использует OpenSSL с набором шифров, который Reddit помечает как «не браузер». Плюс `JA3`-хэш не совпадал с заявленным User-Agent Chrome 120.
Решение — `curl_cffi` с имперсонацией:
```python
from curl_cffi import requests
r = requests.get(
"https://www.reddit.com/r/python.json",
impersonate="chrome120",
proxies={"https": "http://user:pass@[2a01:4f8:c17:1234::1]:8080"},
)
```
`curl_cffi` подделывает JA3 и HTTP/2 fingerprint. После перехода капча исчезла на 200+ запросов. Задержка выросла на 40-60 мс из-за эмуляции.
Сравнение подходов
| Подход | Стабильность | Скорость | Сложность |
|---|---|---|---|
| Прямой IPv4 | Высокая | 80-120 мс | Низкая |
| IPv6 без sticky | Низкая | 150-300 мс | Низкая |
| IPv6 + sticky + apex | Высокая | 180-250 мс | Средняя |
| IPv6 + curl_cffi | Высокая | 220-320 мс | Высокая |
Цифры — медиана по 1000 запросов из Франкфурта к Reddit. Прямой IPv4 быстрее, но не масштабируется. IPv6 с правильной настройкой даёт приемлемую задержку и огромный пул адресов.
Что делать с apex на практике
Apex-запись нужна по двум причинам. Первая — пользователи набирают её руками, и редирект должен остаться внутри прокси. Вторая — API и часть путей идут именно через apex без редиректа.
Схема, которая работает:
1. Прокси слушает и `reddit.com`, и `www.reddit.com` на IPv6
2. SNI всегда `www.reddit.com` для upstream
3. `Host` header — тоже `www.reddit.com`
4. `proxy_redirect` переписывает Location обратно на apex
5. `Alt-Svc` вырезается
6. Sticky session минимум 15 минут
Настройка на стороне клиента — принудительный IPv6 через `curl -6` или конфиг браузера. Если клиент умеет happy eyeballs, он сам выберет IPv6, когда прокси его поддерживает.
Мониторинг и метрики
Без мониторинга ты не узнаешь, что прокси деградировал. Минимум — логировать:
- Код ответа (403/429 — красный флаг)
- Время ответа (p50, p95, p99)
- IP, с которого ушёл запрос
- Наличие `Alt-Svc` в ответе
Простой скрипт на Python:
```python
import time
import requests
PROXY = "http://user:pass@[2a01:4f8:c17:1234::1]:8080"
URL = "https://www.reddit.com/r/python.json"
for i in range(100):
t0 = time.time()
try:
r = requests.get(URL, proxies={"https": PROXY}, timeout=10,
headers={"User-Agent": "monitor/1.0"})
dt = (time.time() - t0) * 1000
print(f"{i} {r.status_code} {dt:.0f}ms alt-svc={r.headers.get('Alt-Svc', '-')}")
except Exception as e:
print(f"{i} ERR {e}")
time.sleep(1)
```
Если p95 выше 500 мс или 403 появляется чаще раза на 100 запросов — прокси или настройки пора менять.
Итог
Reddit через IPv6-прокси работает, но требует аккуратности. Apex-запись — не просто редирект, а точка контроля над всей цепочкой. Sticky session, правильный SNI, вырезанный `Alt-Svc`, эмуляция TLS fingerprint — минимальный набор, чтобы не получать 403 каждые пять минут. Один раз настроишь — дальше стабильно. Через lexic.ml такие пулы поднимаются с готовыми sticky-сессиями и IPv6-выходами, что снимает половину описанных граблей.