SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-26

Низкая скорость WireGuard: причины и способы решения

Низкая скорость WireGuard часто связана с неверным MTU. Узнайте, как выполнить бисектрису пути, настроить TCP MSS и проверить steal time на VPS для устранения зависаний.

Откуда на самом деле берется низкая скорость WireGuard

Низкая скорость WireGuard обычно вызвана одной из четырех причин, вероятность которых неодинакова. Первая — это MTU (maximum transmission unit): туннель формирует пакеты, которые оказываются слишком большими для одного из узлов на пути следования, из-за чего передача больших объемов данных зависает, в то время как мелкие запросы проходят нормально. Вторая — сам маршрут, который был узким местом еще до создания туннеля. Третья — нагрузка на CPU на небольшом VPS, где шифрование конкурирует за ресурсы с другими виртуальными машинами на том же хосте. Четвертая — качество соединения самого пира.

Проверяйте их именно в этом порядке. MTU стоит первым, так как это единственная причина из списка, которую создает сам WireGuard, и потому что ее симптомы не похожи на обычную медленную работу. Неверно настроенный MTU обычно проявляется так: туннель мгновенно устанавливается, отвечает на ping, позволяет войти по SSH, но зависает при первой же попытке скопировать файл.

Один симптом стоит исключить до начала всех проверок. Если каждый новый сайт открывается с задержкой в несколько секунд, а затем загружается на полной скорости, проблема заключается в разрешении имен, а не в пропускной способности. У DNS over WireGuard есть свои специфические сбои, и изменение MTU их не исправит.

Почему MTU в WireGuard равен 1420?

Каждый пакет, который вы отправляете в туннель, шифруется и упаковывается в новый пакет. На эту «обертку» расходуются байты, которые вычитаются из полезной нагрузки.

Заголовок данных WireGuard занимает 32 байта: 4 байта на поле типа, 4 байта на индекс получателя, 8 байт на счетчик и 16 байт на тег аутентификации Poly1305. Вокруг этого располагается UDP-заголовок размером 8 байт. Снаружи находится внешний IP-заголовок, который составляет 20 байт для IPv4 и 40 байт для IPv6. Таким образом, общая инкапсуляция составляет 60 байт, если ваш Endpoint — это IPv4-адрес, и 80 байт, если это IPv6. На странице протокола WireGuard задокументирована структура сообщения, из которой получены эти числа.

wg-quick не занимается догадками. Он считывает MTU интерфейса, через который идет маршрут к вашему Endpoint, а затем вычитает 80. На обычном Ethernet-канале с размером 1500 байт это дает 1420 — значение, которое выводит ip link show wg0. Он вычитает 80, а не 60, чтобы это число оставалось безопасным, даже если конечная точка будет достигнута через IPv6, где внешний заголовок на 20 байт больше.

ChartMTU arithmetic for common underlays
The data behind this chart
[
  {
    "label": "Ethernet, IPv4 endpoint",
    "path_mtu": 1500,
    "encap_overhead": 60,
    "usable_wg0_mtu": 1440
  },
  {
    "label": "Ethernet, IPv6 endpoint",
    "path_mtu": 1500,
    "encap_overhead": 80,
    "usable_wg0_mtu": 1420
  },
  {
    "label": "PPPoE DSL, IPv4 endpoint",
    "path_mtu": 1492,
    "encap_overhead": 60,
    "usable_wg0_mtu": 1432
  },
  {
    "label": "Extra tunnel in the path",
    "path_mtu": 1400,
    "encap_overhead": 80,
    "usable_wg0_mtu": 1320
  }
]

Эти 4 строк — результат арифметических вычислений, а не измерений. На чистом канале с размером 1500 байт и IPv4-адресом конечной точки значение 1440 подошло бы идеально, поэтому значение по умолчанию 1420 оставляет 20 байт неиспользованными. Этот запас предусмотрен намеренно, и он не должен вас беспокоить.

Последняя строка — самая проблемная. Если какой-либо канал на пути пропускает только 1400 байт, туннель, настроенный на 1420, будет создавать внешние пакеты размером 1500 байт для каждого сегмента максимального размера. Это на 100 байт больше, чем может принять такой канал. Подходящее значение в этом случае — 1320.

Не копируйте и значение 1320. MTU вашего пути — это характеристика самого пути, и единственный способ узнать его — измерить.

Как выглядят проблемы с MTU

Сбой происходит не постепенно. Наблюдается четкое разделение между маленькими и большими пакетами.

  • ping через туннель работает с любым стандартным размером.
  • Вход по SSH проходит успешно, ввод команд ощущается отзывчивым.
  • curl -I https://example.com мгновенно возвращает заголовки.
  • curl https://example.com на большой странице зависает после получения первых нескольких килобайт.
  • scp большого файла начинается, а затем останавливается на определенном проценте.
  • SSH-сессия зависает в момент выполнения команды, которая выводит большой объем данных.

Все это происходит потому, что TCP-соединение начинает передавать сегменты полного размера только при наличии большого объема данных. Рукопожатие и первый запрос укладываются в любой MTU на пути следования. Зависание начинается на первом же сегменте полного размера, поэтому соединение выглядит исправным ровно до того момента, пока не становится бесполезным.

Внешний пакет, размер которого превышает MTU следующего звена, ожидает одна из двух судеб.

Фрагментация. Маршрутизатор разбивает пакет, а удаленная сторона собирает его части. Передача работает, но медленнее, так как теперь вы платите двумя пакетами за один, а получатель хранит состояние до прибытия обеих частей. Потеря одного фрагмента приводит к потере всего исходного пакета, поэтому канал с 1% потерь ведет себя значительно хуже. Многие межсетевые экраны также отбрасывают IP-фрагменты согласно политике безопасности, что превращает этот сценарий в следующий.

Отбрасывание, о котором вы можете не узнать. Маршрутизатор, который не выполняет фрагментацию, отправляет отправителю сообщение ICMP (internet control message protocol) «fragmentation needed», содержащее допустимый размер MTU. Если это сообщение доходит, работает механизм path MTU discovery и отправитель самостоятельно уменьшает размер сегмента. Многие сети фильтруют ICMP, поэтому сообщение часто не доходит. Ничто другое не сообщает о потере. Это и есть «черная дыра»: пакет уходит, ответ не приходит, в журналах на обоих концах нет ошибок, а передача зависает до истечения времени ожидания.

Как определить подходящее значение MTU?

Измерьте путь, а затем выполните вычитание. Тестируйте базовую сеть, а не туннель: отправляйте ping на публичный адрес сервера с клиента, запретив фрагментацию.

ping -M do -s 1472 -c 3 203.0.113.10

-M do устанавливает бит DF (don't fragment), поэтому ни один маршрутизатор на пути не сможет разделить пакет. -s — это размер полезной нагрузки ICMP. Полный пакет IPv4 состоит из этой нагрузки, 8 байт заголовка ICMP и 20 байт заголовка IP, поэтому -s 1472 отправляет в сеть ровно 1500 байт.

Важны три результата. Успешные ответы означают, что 1500 байт проходят и MTU не является проблемой. Локальная ошибка означает, что ваш собственный интерфейс уже меньше, чем запрошенный размер:

ping: local error: message too long, mtu=1500

Ответ от промежуточного маршрутизатора сразу дает вам готовый ответ, на этом можно остановиться:

From 203.0.113.1 icmp_seq=1 Frag needed and DF set (mtu = 1492)

Потеря 100% пакетов при 1472 байтах и успешные ответы при меньшем размере указывают на «черную дыру». Маршрутизаторы не присылают уведомлений, поэтому ищите границу методом деления пополам. Возьмите одно значение, которое работает, и одно, которое не работает, протестируйте середину и смещайте соответствующую границу. Каждый шаг сокращает диапазон вдвое, поэтому пяти-шести итераций достаточно.

Пример поиска методом деления пополам

Каждая строка — это команда, которую вы запускаете на клиенте для публичного адреса сервера. В комментарии указан результат.

ping -M do -s 1472 -c 3 203.0.113.10   # 100% loss, so 1500 is too big
ping -M do -s 1272 -c 3 203.0.113.10   # replies, so 1300 fits
ping -M do -s 1372 -c 3 203.0.113.10   # replies, so 1400 fits
ping -M do -s 1422 -c 3 203.0.113.10   # 100% loss, so 1450 is too big
ping -M do -s 1397 -c 3 203.0.113.10   # 100% loss, so 1425 is too big
ping -M do -s 1384 -c 3 203.0.113.10   # 100% loss, so 1412 is too big

Максимальный размер полезной нагрузки, который прошел — 1372, значит, этот путь поддерживает не менее 1400 байт, но менее 1412. Возьмите безопасное значение. MTU пути 1400 минус 80 байт на инкапсуляцию дает MTU для wg0, равный 1320.

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

tracepath -n 203.0.113.10

В последней строке вывода будет указан результат:

     Resume: pmtu 1492 hops 12 back 12

Относитесь к обоим инструментам как к отправной точке, а не как к доказательству. Некоторые хосты ограничивают скорость или полностью блокируют ICMP, поэтому метод деления пополам может показать MTU меньше реального. Настоящая проверка — это передача данных, которая ранее завершалась с ошибкой.

Сначала примените значение «на лету», чтобы в случае ошибки его можно было легко отменить одной командой:

sudo ip link set mtu 1320 dev wg0

Повторите передачу данных, которая зависала. Если она завершилась успешно, сделайте значение постоянным в блоке [Interface] на клиенте:

[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
MTU = 1320
sudo wg-quick down wg0 && sudo wg-quick up wg0
ip link show wg0

ip link show wg0 теперь должен выводить mtu 1320. Если по-прежнему отображается старое значение, значит wg-quick не прочитал отредактированный файл. Проверьте, что вы редактировали именно /etc/wireguard/wg0.conf и что MTU находится в секции [Interface], а не [Peer], где этот параметр игнорируется.

MTU — это свойство конкретного интерфейса, оно не согласовывается между узлами. Установка MTU только на клиенте уменьшает размер пакетов, которые отправляет клиент. Сервер продолжает формировать пакеты согласно своему MTU для wg0, поэтому загрузка данных может по-прежнему уходить в «черную дыру» даже после того, как отправка заработала. Установите значение на обоих концах или используйте clamp MSS на сервере.

Почему MSS clamping исправляет только TCP

Если сервер пересылает трафик для своих узлов, что происходит в любой стандартной настройке WireGuard VPS с использованием NAT (network address translation), одно правило исправляет TCP для всех узлов и избавляет вас от необходимости подбирать значение на каждом клиенте, который вы не контролируете.

MSS (maximum segment size) — это опция TCP, которую каждая сторона указывает в своем SYN-пакете, чтобы сообщить, какой размер сегмента она готова принять. Clamping перезаписывает эту опцию «на лету» в соответствии с реальным MTU маршрута, поэтому обе стороны договариваются о меньшем размере сегмента до начала передачи данных. Это работает, так как происходит во время рукопожатия (handshake) и не зависит от ICMP-сообщений, которые путь, вероятно, блокирует.

В nftables добавьте эту таблицу в /etc/nftables.conf ниже ваших существующих таблиц:

table inet mangle {
  chain forward {
    type filter hook forward priority mangle; policy accept;
    tcp flags syn tcp option maxseg size set rt mtu
  }
}

Перезагрузите конфигурацию с помощью sudo systemctl reload nftables. На системе с iptables эквивалент состоит из одной строки:

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Убедитесь, что правило находится на пути следования пакетов. Запустите sudo nft list table inet mangle или sudo iptables -t mangle -L FORWARD -n -v в момент, когда клиент открывает новые соединения, и следите за ростом счетчика. Если счетчик остается на нуле, значит, пакеты не проходят через этот хук, и правило не работает.

Теперь о реальных ограничениях. Clamping затрагивает только TCP и ничего больше, причем только пересылаемый (forwarded) трафик. Сервис, запущенный на самом WireGuard-сервере, не проходит через хук forward и не подвергается clamping. Это также влияет только на соединения, открытые после загрузки правила: существующие сессии сохраняют MSS, о котором они уже договорились.

UDP остается без изменений, так как в UDP нет рукопожатия, которое можно было бы перезаписать. Большая часть UDP-трафика проходит без проблем. QUIC, транспорт для HTTP/3, самостоятельно проверяет допустимый размер пакета и намеренно начинает с малых значений. Проблемы возникают с UDP, который отправляет один крупный датаграмма и ожидает его доставки, например, ответ DNSSEC (DNS security extensions) размером более 1400 байт. Такие запросы по истечении времени ожидания повторяются через TCP, что для пользователя выглядит как медленная загрузка сайта, а не как полная неработоспособность.

Является ли процессор моего VPS узким местом?

WireGuard использует шифрование ChaCha20-Poly1305 и обмен ключами Curve25519. В пути передачи данных нет AES, что приводит к одному частому заблуждению: инструкции AES-NI в вашем процессоре никак не ускоряют работу WireGuard. Наличие поддержки AES-NI у хоста не дает преимуществ в производительности WireGuard. Алгоритм ChaCha20 был выбран из-за высокой скорости программной реализации, в том числе на процессорах без аппаратного ускорения криптографии.

Это не значит, что WireGuard не потребляет ресурсы. На VPS с 1 vCPU одно ядро обрабатывает и шифрование, и сетевые прерывания, помимо задач вашего приложения.

Проведите замеры во время передачи данных:

sudo apt install -y sysstat
mpstat -P ALL 1

Изучите три столбца. %soft — это время softirq, где происходит обработка пакетов ядром. %steal — время, которое гипервизор отдал другим виртуальным машинам. %idle — оставшийся ресурс.

Значение %soft близкое к 100 на единственном ядре означает, что сервер достиг предела обработки пакетов; это реальное ограничение, которое можно снять добавлением ядер. top показывает ksoftirqd/0 в верхней части списка процессов в тот же момент, что подтверждает тот же вывод с другой стороны.

%steal выше нескольких процентов означает, что проблему нельзя решить на вашей стороне, так как хост переподписан (oversubscribed) и ваш vCPU ожидает физическое ядро. Это часто встречается на самых дешевых тарифах и меняется в течение дня. Steal time от «шумного соседа» требует отдельного анализа, и никакой MTU здесь не поможет.

Еще один фактор — используемая реализация на клиенте. Модуль ядра Linux обеспечивает быстрый путь и распределяет шифрование одного пира по разным ядрам. wireguard-go, реализация в пространстве пользователя, работает медленнее; именно она используется в macOS и iOS, так как эти платформы не позволяют приложениям загружать модули ядра.

Это проблема пути или самого линка узла?

Перед внесением любых настроек получите два показателя с одного клиента с интервалом в несколько минут: пропускную способность без туннеля и с ним. Без этих данных вы будете действовать наугад.

Запустите iperf3 -s на сервере. Для прямого теста необходимо, чтобы порт TCP 5201 был доступен на публичном адресе, поэтому откройте его на время проверки и удалите правило после завершения. Убедитесь, что порт снова закрыт, а не просто предполагайте это.

# on the client, outside the tunnel
iperf3 -c 203.0.113.10
# then through the tunnel
iperf3 -c 10.8.0.1

Если показатели близки, WireGuard потребляет минимум ресурсов, а ограничением является сам путь. Если скорость в туннеле значительно ниже прямой, а %soft остаётся низким, вернитесь к настройке MTU. Фрагментация снижает пропускную способность, не вызывая сбоев, поэтому она проявляется как процентная потеря скорости, а не как зависание.

Тестируйте оба направления, так как домашние подключения обычно асимметричны. iperf3 -c 10.8.0.1 -R меняет направление потока, чтобы сервер отправлял данные. Клиент на канале 500/20 Мбит/с никогда не передаст в туннель больше своих 20 Мбит/с на отдачу, и никакие изменения на стороне сервера это не исправят.

Затем протестируйте с параллельными потоками:

iperf3 -c 10.8.0.1 -P 4

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

sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem

Третье значение в каждом выводе — это максимум, до которого Linux может автоматически настроить окно. Потеря пакетов также жёстко ограничивает один поток, так как именно на потери реагирует алгоритм управления перегрузкой TCP, а длинный путь делает восстановление затратным. Запустите mtr -rwc 100 203.0.113.10 с клиента на сто циклов, чтобы увидеть, на каком этапе маршрута возникают потери. Потери, которые начинаются на одном узле и сохраняются до конечного, реальны. Потери на промежуточном узле, которые исчезают после него, означают лишь то, что этот маршрутизатор снижает приоритет ICMP, и они не имеют значения.

Чтобы получить воспроизводимую картину состояния самого сервера, отдельно от сети, протестируйте VPS по документированной методике, чтобы вы могли повторить тот же тест после изменений и сравнить результаты в равных условиях.

Чего WireGuard не может исправить

WireGuard — это туннель. Он не может работать быстрее, чем самое медленное звено на пути следования пакетов, а его использование всегда немного снижает скорость соединения.

Он не выполняет сжатие данных. В нем нет аналога comp-lzo из OpenVPN, и внедрение такой функции не планируется, так как сжатие перед шифрованием приводит к утечке информации о незашифрованном тексте. Большинство объемных данных уже сжаты, поэтому на практике вы ничего не теряете. Это одно из реальных различий, которые стоит учитывать при сравнении WireGuard и OpenVPN, и это осознанное проектное решение.

Полноценный туннель меняет маршрут каждого пакета. Трафик, который раньше шел от вас к ближайшей CDN (сеть доставки контента), теперь идет от вас к вашему VPS, а затем к CDN. Если VPS находится на другом континенте, каждый запрос проходит этот обходной путь, и время кругового обращения (RTT) соответственно увеличивается. Никакой параметр конфигурации не может это сократить. Перенесите VPS ближе или используйте раздельное туннелирование (split tunnel), чтобы длинный путь проходил только тот трафик, которому нужен VPN. Решение о том, какой трафик куда направляется, полностью определяется AllowedIPs, и cryptokey routing объясняет, как принимается это решение.

Ограничения провайдера действуют вне туннеля, и о них легко забыть. Тарифный план с ежемесячным лимитом трафика часто ограничивает скорость порта до гораздо меньших значений после исчерпания лимита, и туннель начинает выглядеть нерабочим. Проверьте панель управления провайдера, прежде чем тратить вечер на настройку MTU.

PersistentKeepalive не влияет на пропускную способность. Этот параметр существует для поддержания активности NAT-сессии, чтобы сервер мог связаться с клиентом, находящимся за домашним роутером. Установка значения ниже 25 секунд лишь увеличивает количество пакетов и ничего не исправляет.

Порядок диагностики

  1. Воспроизведите проблему и определите, происходит зависание или равномерное замедление. Зависание указывает на проблемы с MTU. Равномерное замедление — нет.
  2. Выполните бинарный поиск с помощью ping -M do от клиента до публичного адреса сервера и запишите значение path MTU.
  3. Вычтите 80, установите полученное значение MTU для интерфейса wg0 на обоих концах и повторно протестируйте передачу данных, которая завершалась с ошибкой.
  4. Добавьте MSS clamping на сервере, если он пересылает трафик для пиров.
  5. Запустите mpstat -P ALL 1 во время передачи данных и проанализируйте %soft и %steal.
  6. Запустите iperf3 вне туннеля и внутри него в обоих направлениях, используя один поток и флаг -P 4.
  7. Запустите mtr -rwc 100 до сервера и проверьте наличие потерь пакетов, которые сохраняются до последнего узла.

Меняйте тарифный план сервера только после того, как на шаге 5 выяснится, что предел производительности упирается в CPU. Шаги с 1 по 4 бесплатны и решают большинство проблем с низкой скоростью туннеля.

FAQ

Почему мой туннель WireGuard быстро передает ping, но медленно скачивает файлы?

Такое поведение — типичный признак проблемы с MTU. Маленькие пакеты проходят через любые узлы на пути, поэтому ping и SSH-сессии работают корректно. При передаче больших объемов данных отправляются сегменты максимального размера. Инкапсулированная версия таких сегментов превышает допустимый размер для некоторых узлов сети. Если маршрутизатор отбрасывает их, не отправляя обратно ICMP-сообщение, потеря пакетов не фиксируется, и передача зависает. Определите MTU пути с помощью метода бисекции ping -M do до публичного адреса сервера, вычтите 80 байт на инкапсуляцию и установите полученное значение в качестве MTU для wg0 на обоих концах.

Какое значение MTU следует установить для WireGuard?

Универсального числа не существует, именно поэтому значение по умолчанию 1420 подходит не всем. 1420 — это 1500 минус 80 байт на заголовки WireGuard, UDP и внешний заголовок IPv6. Если ваш канал пропускает менее 1500 байт, что типично для PPPoE DSL и любых соединений, где трафик проходит через другие туннели, вам потребуется меньшее значение. Сначала измерьте MTU пути, а затем вычтите из него 80.

Заменяет ли MSS clamping настройку MTU?

Нет. Clamping перезаписывает опцию MSS в процессе TCP-рукопожатия, заставляя обе стороны отправлять сегменты меньшего размера, что исправляет работу TCP без изменения настроек интерфейса. У UDP нет рукопожатия, которое можно было бы перезаписать, поэтому на него это не влияет. Кроме того, clamping применяется только к трафику, который сервер пересылает (forwarding), поэтому сервисы, запущенные на самом сервере WireGuard, не получают преимуществ. Используйте оба метода: корректный MTU на интерфейсе и clamping для клиентов, конфигурацией которых вы не управляете.

Станет ли WireGuard быстрее при переходе на более мощный тариф VPS?

Только если ограничением является процессор, и одна команда поможет это выяснить. Запустите mpstat -P ALL 1 во время передачи данных. Если %soft приближается к 100 на единственном ядре, значит, предел производительности достигнут на этапе обработки пакетов, и дополнительные ядра помогут его повысить. Высокое значение %steal означает, что хост перегружен, поэтому решением будет смена тарифа или хостинг-провайдера. Если оба показателя низкие, а скорость туннеля все равно мала, процессор простаивает, и переход на более мощный тариф ничего не изменит.

Почему мой Mac работает медленнее, чем Linux-клиент в той же сети?

Linux-клиент использует модуль WireGuard, встроенный в ядро, который обрабатывает пакеты на уровне ядра и распределяет шифрование данных одного пира по нескольким ядрам процессора. Приложения для macOS и iOS используют wireguard-go — реализацию в пространстве пользователя (userspace), так как эти платформы не позволяют приложениям загружать модули ядра. Userspace копирует каждый пакет между ядром и приложением, и это копирование снижает пропускную способность. Разница в скорости ожидаема, и никакие настройки клиента не помогут её устранить.