Поднять сервер с Reality и подключиться к нему — задача одного вечера. Настроить так, чтобы банк открывался напрямую, Netflix шёл через прокси, реклама резалась на подлёте, а DNS не рассказывал провайдеру, куда вы ходите — задача другого порядка. Именно здесь ломается больше всего конфигов, и именно про это в подписках от продавцов обычно ничего нет: там просто "final": "proxy" и всё.
Этот гайд разбирает клиентскую часть sing-box по слоям — в том порядке, в котором пакет реально проходит через программу. Актуально для стабильной ветки 1.13.x (последний релиз на момент написания — v1.13.16 от 3 августа 2026; ветка 1.14 пока в бете). Всё, что ниже, взято из официальной документации sing-box.sagernet.org, а не из чужих статей.
Если вы ещё не разбирались, как устроен сам транспорт, начните с соседней темы — как работает Reality (XTLS) и почему он обходит DPI.
Слой 0. Сначала решите стратегию, потом пишите JSON
Прежде чем открывать редактор, надо ответить на один вопрос: что происходит с трафиком, который не подошёл ни под одно правило? От ответа зависит вообще всё остальное.
Ключевой принцип: правила описывают исключения, а route.final описывает умолчание. Большинство сломанных конфигов — это попытка описать правилами всё сразу, вместо того чтобы выбрать правильное умолчание и добавить к нему десяток исключений.
Для российского пользователя в 2026-м разумный дефолт — белый список: final указывает на прокси, а direct получают только явно перечисленные российские адреса и домены. Причина простая: список блокировок растёт быстрее, чем вы успеваете его править, а список того, что должно идти напрямую (банки, госуслуги, локальные сервисы, серый IP-диапазон), почти не меняется.
Слой 1. Приём трафика: tun и зачем нужен sniff
Режим tun создаёт виртуальный сетевой интерфейс и забирает на себя весь трафик системы — не только тот, который приложение согласилось отдать в прокси.
{
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
"auto_route": true,
"strict_route": true,
"stack": "mixed"
}
]
}
Что тут важно:
| Поле | Значение | Зачем |
|---|---|---|
address |
список префиксов (с 1.10.0) | Раньше были раздельные inet4_address / inet6_address — в старых гайдах они ещё встречаются |
auto_route |
true |
Прописывает маршрут по умолчанию в туннель |
strict_route |
true |
Заставляет систему уважать эти маршруты; без него часть трафика утекает мимо |
stack |
system / gvisor / mixed |
mixed — компромисс: TCP через system, UDP через gVisor |
auto_redirect |
Linux, с 1.10.0 | Ускоряет маршрутизацию через nftables вместо tun-стека |
На Linux с auto_redirect появляется отдельное действие bypass (добавлено в 1.13.0) — оно возвращает соединение в обычный сетевой стек мимо туннеля. Полезно для трафика, который должен уходить в локальную сеть без накладных расходов tun.
Почему без sniff маршрутизация по доменам не работает
Когда трафик приходит из tun, sing-box видит только адрес назначения — IP и порт. Домена там нет: приложение уже отрезолвило его само. А правила вроде «youtube.com — через прокси» оперируют доменами.
Решает это действие sniff: sing-box заглядывает в начало соединения, вытаскивает SNI из TLS ClientHello, Host из HTTP или ALPN из QUIC — и дальше маршрутизирует уже по домену.
{
"route": {
"rules": [
{ "action": "sniff", "timeout": "300ms" },
{ "protocol": "dns", "action": "hijack-dns" }
]
}
}
Если вы переносите конфиг из версии до 1.11, у вас sniff скорее всего стоит полем внутри inbound ("sniff": true). Это устаревший синтаксис: с 1.11.0 сниффинг стал действием правила. Аналогично исчезли специальные outbound’ы block и dns — они превратились в "action": "reject" и "action": "hijack-dns".
Слой 2. DNS — место, где чаще всего всё и ломается
Симптом знакомый: сайт вроде бы должен идти через прокси, но открывается «российская» версия, или не открывается вовсе. Почти всегда причина в том, что имя резолвилось не тем сервером.
С версии 1.12.0 формат DNS-серверов переписан. Старое поле address со схемой в URL заменено на явные type + server:
| Было (до 1.12) | Стало (1.12+) |
|---|---|
"address": "local" |
"type": "local" |
"address": "1.1.1.1" |
"type": "udp", "server": "1.1.1.1" |
"address": "tls://1.1.1.1" |
"type": "tls", "server": "1.1.1.1" |
"address": "https://1.1.1.1/dns-query" |
"type": "https", "server": "1.1.1.1" |
"address": "quic://1.1.1.1" |
"type": "quic", "server": "1.1.1.1" |
"address": "fakeip" |
"type": "fakeip" с inet4_range / inet6_range |
Рабочая раскладка выглядит так:
{
"dns": {
"servers": [
{
"tag": "dns-proxy",
"type": "https",
"server": "1.1.1.1",
"detour": "proxy"
},
{
"tag": "dns-local",
"type": "udp",
"server": "192.168.1.1",
"detour": "direct"
}
],
"rules": [
{ "rule_set": ["geosite-category-ru"], "server": "dns-local" }
],
"final": "dns-proxy",
"strategy": "prefer_ipv4"
}
}
Смысл: российские домены резолвит роутер (иначе вы получите чужой CDN-узел и потеряете скорость), всё остальное уезжает в DoH через сам прокси — благодаря detour. Провайдер в этом случае видит только зашифрованное соединение с вашим сервером.
detour у DNS-сервера — это и есть защита от DNS-утечек. Без него запросы уходят открытым UDP с вашего адреса, и весь список посещённых доменов остаётся у провайдера, даже если сам трафик потом идёт через туннель. Проверять результат стоит не «на глаз», а на dnsleaktest-подобном сервисе и параллельно tcpdump port 53 на шлюзе.
Типичная ловушка: указать в DoH-сервере доменное имя ("server": "dns.example.com") и отправить его в detour: "proxy". Чтобы подключиться к прокси, нужно отрезолвить его адрес; чтобы отрезолвить — нужен DNS, который ходит через прокси. Круг замкнулся, и sing-box не стартует корректно. Решения два: указывать DoH-сервер по IP, либо задать domain_resolver (появился в 1.12.0) с указанием отдельного bootstrap-сервера.
Ещё одна тонкость, которую полезно знать: resolve — это тоже действие правила, а не свойство inbound. Оно принудительно резолвит домен на нужном сервере в нужную сторону:
{ "action": "resolve", "server": "dns-local", "strategy": "ipv4_only" }
Пригождается, когда провайдер выдаёт кривой IPv6 или когда правило дальше по цепочке матчит по ip_cidr и ему нужен уже готовый адрес.
Слой 3. route.rules: первое совпадение выигрывает
Это ядро всей конструкции. Правила проверяются сверху вниз, срабатывает первое подошедшее, остальные не рассматриваются. Отсюда главное практическое следствие: порядок правил — часть логики, а не косметика.
Набор полей у правила огромный. Вот те, которые реально используются в клиентских конфигах:
| Поле | Что матчит |
|---|---|
domain, domain_suffix, domain_keyword, domain_regex |
Домен — точно, по суффиксу, по подстроке, по регулярке |
ip_cidr, source_ip_cidr |
Адрес назначения / источника |
ip_is_private, source_ip_is_private |
Непубличные адреса (серые сети, localhost) |
port, port_range, source_port, source_port_range |
Порты |
network |
tcp, udp, icmp |
protocol, client |
То, что определил сниффер |
process_name, process_path, package_name |
Приложение: имя процесса (desktop) или пакет Android |
network_type, network_is_expensive |
Wi-Fi / сотовая / Ethernet, лимитная сеть |
wifi_ssid, wifi_bssid |
Конкретная Wi-Fi-сеть |
clash_mode |
Текущий режим в Clash API (Rule / Global / Direct) |
rule_set |
Ссылка на внешний список |
invert |
Инвертировать результат правила |
Действия (action), которые завершают обработку:
route— отправить в outbound (полеoutbound; полеoutboundпрямо в правиле устарело с 1.11.0 в пользуaction);reject— оборвать. Естьmethod:default(TCP RST / ICMP port unreachable),drop(молча выбросить),reply(только для ICMP echo);hijack-dns— увести в DNS-модуль;bypass— вернуть в системный стек (Linux +auto_redirect, с 1.13.0).
И не завершающие: sniff, resolve, route-options. Последнее — это набор тюнинга поверх маршрута: override_address, override_port, udp_timeout, а также tls_fragment и tls_record_fragment (с 1.12.0), которые режут TLS ClientHello на части — иногда этого хватает, чтобы проскочить примитивный DPI без прокси вообще.
Правила можно комбинировать логически: {"type": "logical", "mode": "and", "rules": [...]}. Режимы — and и or. Классический пример: «домен из списка Х и сеть сотовая» — на мобильном интернете гнать через прокси, на домашнем Wi-Fi не гнать.
Слой 4. rule_set: откуда берутся сами списки
Раньше были поля geoip и geosite с монолитными базами. Начиная с 1.8.0 они заменены на rule-set — отдельные списки, которые подключаются по тегу и обновляются независимо.
Три типа:
inline(с 1.10.0) — правила прямо в конфиге, без внешнего файла;local— файл на диске, перечитывается при изменении;remote— скачивается по URL и кешируется.
Официальные сборки лежат в репозиториях SagerNet/sing-geoip и SagerNet/sing-geosite, ветка rule-set:
{
"route": {
"rule_set": [
{
"tag": "geoip-ru",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-ru.srs",
"download_detour": "proxy",
"update_interval": "1d"
},
{
"tag": "geosite-category-ru",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ru.srs",
"download_detour": "proxy"
}
]
},
"experimental": {
"cache_file": { "enabled": true }
}
}
Тег geosite-ru не существует — по этому имени вы получите 404 и молча пустой список. Российская категория в sing-geosite называется geosite-category-ru. Проверить наличие любого списка можно просто открыв URL в браузере: отдаётся файл — тег есть, приходит «404: Not Found» — тега нет. Это первое, что стоит сделать перед тем, как копировать чужой конфиг из чата.
experimental.cache_file.enabled: true — не опция «на будущее», а обязательное условие для remote rule-set. Без неё списки будут перекачиваться при каждом старте: медленно, а на мобильном интернете ещё и дорого.
Свой список собирается из JSON-исходника командой:
sing-box rule-set compile --output my-direct.srs my-direct.json
Исходник выглядит так:
{
"version": 3,
"rules": [
{ "domain_suffix": [".gosuslugi.ru", ".nalog.gov.ru"] }
]
}
Поле version — это версия формата, не вашего файла, и она определяет, какие типы правил вообще можно использовать:
| version | Появилась в | Что добавила |
|---|---|---|
| 1 | 1.8.0 | Исходный формат |
| 2 | 1.10.0 | Оптимизация памяти для domain_suffix |
| 3 | 1.11.0 | Сетевые правила (network_type и родня) |
| 4 | 1.13.0 | Правила по адресам интерфейсов |
| 5 | 1.14.0 | package_name_regex |
Скомпилировали список с version: 5 — старый клиент его не прочитает. Это частая причина загадочного «правило есть, а не работает» после того, как кто-то в чате поделился свежим .srs. Совместимость идёт только снизу вверх.
Если вы переезжаете на 1.14, учтите: download_detour там объявлен устаревшим, вместо него — общий механизм http_client. На 1.13.x он продолжает работать штатно.
Собираем всё вместе
Минимальный, но осмысленный конфиг «белый список» целиком:
{
"log": { "level": "info", "timestamp": true },
"dns": {
"servers": [
{ "tag": "dns-proxy", "type": "https", "server": "1.1.1.1", "detour": "proxy" },
{ "tag": "dns-local", "type": "udp", "server": "192.168.1.1", "detour": "direct" }
],
"rules": [ { "rule_set": ["geosite-category-ru"], "server": "dns-local" } ],
"final": "dns-proxy"
},
"inbounds": [
{ "type": "tun", "tag": "tun-in", "address": ["172.19.0.1/30"],
"auto_route": true, "strict_route": true, "stack": "mixed" }
],
"outbounds": [
{ "type": "vless", "tag": "proxy", "server": "203.0.113.10", "server_port": 443,
"uuid": "ВАШ-UUID", "flow": "xtls-rprx-vision",
"tls": { "enabled": true, "server_name": "www.example.com",
"utls": { "enabled": true, "fingerprint": "chrome" },
"reality": { "enabled": true, "public_key": "ВАШ-PUBLIC-KEY", "short_id": "ВАШ-SHORT-ID" } } },
{ "type": "direct", "tag": "direct" }
],
"route": {
"rules": [
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" },
{ "ip_is_private": true, "action": "route", "outbound": "direct" },
{ "rule_set": ["geosite-ads"], "action": "reject", "method": "drop" },
{ "rule_set": ["geosite-category-ru", "geoip-ru"], "action": "route", "outbound": "direct" }
],
"rule_set": [
{ "tag": "geosite-category-ru", "type": "remote", "format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ru.srs",
"download_detour": "proxy" },
{ "tag": "geoip-ru", "type": "remote", "format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-ru.srs",
"download_detour": "proxy" },
{ "tag": "geosite-ads", "type": "remote", "format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ads-all.srs",
"download_detour": "proxy" }
],
"final": "proxy",
"auto_detect_interface": true
},
"experimental": { "cache_file": { "enabled": true } }
}
Порядок правил здесь и есть логика: сначала техника (сниффинг, перехват DNS), потом локальная сеть, потом резка рекламы, потом «своё — напрямую», и только после этого — умолчание в прокси.
Перед запуском — обязательно два шага:
sing-box check -c config.json # синтаксис и ссылки на несуществующие теги
sing-box format -c config.json -w # привести к каноническому виду
check ловит большинство опечаток в тегах — самую частую причину «конфиг вроде правильный, а трафик идёт не туда».
Когда правило не срабатывает: порядок отладки
- Проверьте порядок. Правило ниже более общего никогда не выполнится.
ip_is_privateв конце списка бесполезен. - Проверьте, есть ли вообще домен. Нет действия
sniff— нет доменов, и всеdomain_suffixмертвы. - Поднимите уровень лога до
debugи посмотрите, какой outbound выбран для конкретного соединения. Там же видно, отработал ли сниффер. - Проверьте сам список —
sing-box rule-set matchпокажет, попадает ли конкретный домен в.srs, не заставляя вас гадать. - Проверьте, что список скачался. Remote rule-set без
cache_fileи без доступа к GitHub — это пустой список, который молча ничего не матчит.
Отдельная категория — приложения, которые вас игнорируют: браузеры с DoH внутри себя, мессенджеры с захардкоженными резолверами, устройства с прошитым 8.8.8.8. Их лечат правилом {"port": 53, "action": "hijack-dns"} плюс блокировкой исходящего DoH — но полностью закрыть эту дыру на клиенте нельзя, только на шлюзе.
Чего этот подход не сделает
Скепсис по делу, чтобы не было завышенных ожиданий.
Гео-списки врут. geoip-ru — это база принадлежности IP, и она отстаёт от реальности. Российский сервис за иностранным CDN в неё не попадёт, а иностранный сервис на российском хостинге попадёт зря. Готовьтесь дополнять список руками через inline rule-set.
CDN общий на всех. Один и тот же адрес Cloudflare или Akamai обслуживает и заблокированный, и разрешённый ресурс. Правило по IP тут бессильно в принципе — спасает только маршрутизация по домену, то есть работающий sniff.
Маршрутизация не лечит блокировку самого протокола. Если DPI душит ваше соединение с сервером, никакие правила на клиенте не помогут — проблема ниже по стеку. Это отдельная тема, разобранная в диагностике блокировок по симптомам.
Реклама режется не вся. reject на уровне соединения не трогает то, что приходит с того же домена, что и контент — YouTube тому примером.
Источники (всё — официальная документация проекта)
- sing-box: Route Rule
- sing-box: Rule Action
- sing-box: Rule Set и Source Format
- sing-box: DNS
- sing-box: TUN inbound
- sing-box: Migration — что менялось в 1.10–1.14
- sing-box: Changelog
Какой стратегии придерживаетесь вы — белый список, чёрный или «всё через прокси и не думать»? И интересен практический опыт: чем дополняете geosite-category-ru / geoip-ru руками, какие сервисы регулярно вылетают из этих списков и ломаются?


