MAATRIX / Блог / Плата за исходящий трафик: как её незаметно накрутить

Плата за исходящий трафик: как её незаметно накрутить

MAATRIX

Провайдер прислал счёт за трафик, который вдвое-втрое больше обычного, а вы ничего не меняли в коде и не запускали новых фич. Знакомая ситуация: почти всегда виноват не рост аудитории, а рутинная техническая недоработка, которая годами раздаёт лишние байты — просто раньше это было незаметно. Разберём пять типовых мест утечки и как их найти у себя за 10 минут.

Почему это вообще больно

На VPS с щедрым или безлимитным трафиком (как большинство современных тарифов) перерасход просто незаметен — сервер отдаёт условные 50 ГБ вместо нужных 15, и никто не считает разницу. А вот у провайдеров с потарифной оплатой исходящего трафика (объектные хранилища, CDN, часть облачных площадок, трафик между регионами у больших облаков) каждый лишний гигабайт — это строчка в счёте. Проблема в том, что причины раздувания одни и те же независимо от модели оплаты — просто в одном случае они бьют по деньгам сразу, а в другом копятся незаметно и всплывают, когда вы решите перенести часть инфраструктуры на тарифный сервис или подключить CDN с оплатой за трафик.

Дальше — пять конкретных сценариев, из-за которых трафик раздувается в разы, и как проверить каждый у себя.

Сценарий 1: нет сжатия ответов сервера

Если nginx или ваше приложение отдают HTML, CSS, JS, JSON без gzip/brotli — вы гоняете в 3-10 раз больше байт, чем нужно. Текстовые форматы сжимаются отлично: страница в 200 КБ без сжатия превращается в 30-40 КБ с gzip и ещё меньше с brotli.

Проверка — одна команда:

curl -s -o /dev/null -w '%{size_download}\n' -H 'Accept-Encoding: gzip' https://example.com/
curl -s -o /dev/null -w '%{size_download}\n' https://example.com/

Если числа совпадают — сжатие не работает, хотя заголовок был отправлен. Проверьте, что реально пришло:

curl -sI -H 'Accept-Encoding: gzip, br' https://example.com/ | grep -i content-encoding

Пусто в ответе — сжатие выключено или настроено криво (частая ошибка — gzip включён глобально, но gzip_types не перечисляет нужный MIME-тип, и JSON/SVG уходят как есть). Минимальный рабочий конфиг nginx:

gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript
           text/xml application/xml application/xml+rss text/javascript
           image/svg+xml;

Brotli даёт дополнительные 10-20% к gzip на текстовых форматах, но требует модуля (ngx_brotli, собирается отдельно или ставится из репозитория дистрибутива):

brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript
             text/xml application/xml image/svg+xml;

Отдельная ловушка: сжатие уже упакованных форматов (JPEG, MP4, ZIP, WOFF2) бесполезно и только тратит CPU — не добавляйте их в gzip_types/brotli_types, они и так плотные.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Сценарий 2: клиент не кеширует статику — файлы качаются заново

Если у CSS/JS/шрифтов/картинок нет заголовков кеширования или стоит Cache-Control: no-cache там, где это не нужно, браузер каждого посетителя перекачивает одни и те же файлы при каждом визите и даже при переходе между страницами сайта. Для сайта с постоянной аудиторией это легко удваивает трафик статики.

Проверка:

curl -sI https://example.com/assets/app.css | grep -iE 'cache-control|expires|etag'

Если заголовков нет вообще — браузер полагается на эвристику самого браузера, что непредсказуемо. Правильная схема — версионировать файлы (хэш в имени: app.a1b2c3.css) и ставить долгий кеш на неизменяемые ассеты:

location ~* \.(css|js|woff2?|svg|png|jpe?g|webp|avif)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

location = /index.html {
    add_header Cache-Control "no-cache";
}

Ключевая идея: HTML — короткий кеш или no-cache (он лёгкий и должен обновляться), а версионированная статика — год и immutable, потому что при изменении файла меняется и его имя, повторной загрузки при обновлении не потребуется. Если версионирования нет и имена файлов постоянные, ставьте кеш короче (часы, не год), иначе пользователи будут видеть старую статику после деплоя.

Отдельно проверьте ETag/If-None-Match — на статике без версионирования это хоть какая-то защита от полной перекачки при повторном визите:

curl -sI https://example.com/logo.png | grep -i etag

Сценарий 3: изображения и видео в оригинальном весе

Самый частый источник лишнего трафика на контентных и e-commerce сайтах: в CMS или на диске лежит оригинал с камеры или из фотостока (4-8 МБ на изображение, необработанное видео), а на страницу он отдаётся как есть, без адаптации под веб.

Быстрая ревизия каталога:

find /var/www/example.com/uploads -type f \( -iname '*.jpg' -o -iname '*.png' \) -size +500k \
  -exec ls -lh {} \; | sort -k5 -h -r | head -20

Это покажет самые тяжёлые файлы, которые реально отдаются посетителям. Дальше — три независимых шага:

  • Пересжать существующее. Для JPEG/PNG — jpegoptim, optipng, pngquant; для конвертации в современные форматы — cwebp/avifenc. Пример пакетного пересжатия:
find . -iname '*.jpg' -exec jpegoptim --max=80 --strip-all {} \;
  • Отдавать правильный размер, а не оригинал. Картинка 4000×3000 для блока шириной 400px в вёрстке — чистый перерасход в десятки раз. Нужны отресайженные варианты под srcset или imgproxy/thumbnail-сервис на лету.
  • Проверить видео отдельно. Необработанное видео с телефона или экрана — это битрейт, рассчитанный не под веб-стриминг. Перекодировка через ffmpeg с разумным битрейтом обычно уменьшает файл в разы без заметной потери качества:
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset slow -c:a aac -b:a 128k output.mp4

Ориентир (именно ориентир, у вас цифры будут другими в зависимости от контента): переход с оригиналов на оптимизированные веб-форматы обычно сокращает вес медиа в 2-5 раз. Точную экономию у себя вы увидите только измерив: посчитайте суммарный вес каталога uploads до и после через du -sh.

Сценарий 4: публичный эндпоинт раздаёт бэкап или большой файл всем подряд

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

Типичные места, где это всплывает:

  • бакет объектного хранилища с публичным ACL, где лежат бэкапы;
  • /backup/, /dump.sql, /wp-content/uploads/backup-*.zip — доступны напрямую по URL без авторизации;
  • директория со статическими файлами, где autoindex on в nginx показывает листинг каталога с бэкапами внутри.

Проверка листинга и прямого доступа:

curl -s https://example.com/backup/ | grep -i 'index of'
curl -sI https://example.com/backup/dump.sql.gz

Если возвращается 200 OK и реальный размер файла — это открытый эндпоинт, и сканеры интернета найдут его без вашего участия за считаные дни. Смотрите логи на предмет повторяющихся скачиваний одного и того же тяжёлого файла:

awk '{print $7, $10}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Первая колонка — сколько раз запрашивался URL, вторая — размер ответа. Один URL с большим размером и сотнями обращений — характерный след утечки.

Что делать:

  • закрыть autoindex (autoindex off; в nginx, это должно быть по умолчанию, но панели управления иногда включают его сами);
  • убрать бэкапы из веб-корня — им там вообще не место, хранить отдельно от document root с ограничением доступа по IP или авторизацией;
  • на объектных хранилищах — явно проверить ACL бакета, публичный доступ должен быть осознанным исключением, а не дефолтом;
  • добавить robots.txt с запретом индексации служебных путей — это не защита от целенаправленной атаки, но снижает случайный трафик от легитимных краулеров.

Сценарий 5: репликация и бэкапы идут через дорогой канал вместо локального

Если бэкапы или репликация базы уходят в другой регион или к другому провайдеру напрямую через публичный интернет, вы платите за межрегиональный/межконтинентальный исходящий трафик там, где могло бы быть либо дёшево (внутри одного дата-центра/региона), либо вообще бесплатно (через приватную сеть провайдера).

Проверить, куда реально идёт трафик, можно через vnstat или iftop, глядя на адресата:

vnstat -i eth0 --json | jq '.interfaces[0].traffic'
iftop -i eth0 -P

И сверить IP назначения с тем, в каком регионе физически стоит адресат бэкапа/реплики. Частые причины дорогого пути:

  • бэкап настроен на S3-совместимое хранилище в другом континенте, хотя рядом с сервером есть локальное или хотя бы внутрирегиональное хранилище;
  • репликация БД идёт напрямую между серверами разных провайдеров вместо промежуточного сжатия или через VPN-туннель с меньшим числом ретрансляций;
  • нет сжатия самого бэкапа перед отправкой (pg_dump | gzip вместо pg_dump в чистом виде — экономия обычно кратная для текстовых дампов SQL).

Практические шаги:

# сжатие дампа перед отправкой куда угодно
pg_dump mydb | gzip -9 > mydb_$(date +%F).sql.gz

# rclone с ограничением полосы, чтобы не забить канал и контролировать объём
rclone sync /backups remote:bucket --bwlimit 10M --transfers 4

Если провайдер бэкап-хранилища и провайдер основного сервера разные — посчитайте, что дешевле: держать хранилище рядом (тот же регион, часто внутренний трафик между сервисами одного провайдера не тарифицируется или стоит на порядок дешевле) или платить за трансконтинентальный исходящий каждую ночь. Для реплик PostgreSQL и подобных систем разница особенно заметна — это непрерывный поток, а не разовая передача, и он копится сутками.

Чек-лист: где искать лишний трафик у себя

Пройдитесь по пунктам за один присест — большинство проверок занимают пару минут:

ПроверкаКомандаЧто ищем
Сжатие ответов`curl -sI -H 'Accept-Encoding: gzip,br' URL \grep -i content-encoding`Пустой ответ = сжатие не работает
Кеш статики`curl -sI URL/app.css \grep -i cache-control`Нет заголовка или no-cache на неизменяемых файлах
Тяжёлые изображенияfind uploads -size +500kОригиналы без адаптации под веб
Открытый листинг`curl -s URL/backup/ \grep -i 'index of'`autoindex on на служебных путях
Топ по трафику в логах`awk '{print $7,$10}' access.log \sort \uniq -c \sort -rn`Один URL с большим весом и частыми обращениями
Куда уходит трафикvnstat/iftopМежрегиональный адресат для бэкапов/реплики
Вес дампа до отправкиdu -sh dump.sql*Сжимаете ли вы бэкап перед отправкой

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

С чего начать, если времени мало и нужно быстро сократить счёт?

С проверки сжатия (сценарий 1) и открытых эндпоинтов (сценарий 4) — первое чинится одной строкой в конфиге nginx и сразу даёт эффект на всём трафике, второе может останавливать разовую, но крупную утечку.

Насколько сильно gzip/brotli реально экономят трафик?

Зависит от типа контента: текстовые форматы (HTML, CSS, JS, JSON, SVG) сжимаются заметно, обычно в несколько раз; уже сжатые форматы (JPEG, MP4, WOFF2, ZIP) — почти не сжимаются повторно, включать для них gzip бессмысленно и тратит CPU впустую.

Если VPS с безлимитным трафиком — вообще не нужно этим заниматься?

Разгрузка сервера всё равно снижает нагрузку на CPU/диск и ускоряет отдачу для пользователей, но да, финансовой мотивации меньше. Стоит держать в порядке на случай, если часть инфраструктуры позже переедет на тарифный сервис (CDN, объектное хранилище, другой регион).

Как понять, что проблема именно в трафике, а не в чём-то другом, если трафик резко упал или пропал после переезда?

Это отдельная история — там чаще виноваты DNS, маршрутизация или неверно перенесённые правила firewall, а не перерасход. Сначала сверьте объём запросов в логах до и после переезда, а не только байты.

Автоматизация проверок — есть смысл ставить мониторинг?

Да, если объём трафика заметен в бюджете — простой ежедневный снимок через vnstat с алертом при аномальном скачке ловит и утечки, и разовые инциденты (DDoS, забытый краулер) быстрее, чем ручная проверка раз в месяц.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →