Docker & DevOps 13 мин чтения 2026-10-01

Свой сервис коротких ссылок со статистикой и кастомным доменом: развертывание Shlink и Shlink Web Client в Docker Compose

Инструкция по развертыванию высокопроизводительного сервиса коротких ссылок Shlink и веб-панели Shlink Web Client в Docker Compose. Настройка кастомного домена, базы данных MariaDB, Caddy и Nginx с авто-SSL, отслеживание геолокации через GeoLite2, генерация QR-кодов и управление кодами редиректа 301, 302 и 307.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

Публичные сервисы сокращения ссылок вроде Bitly, TinyURL или Clck привычны большинству маркетологов и разработчиков, но несут скрытые системные риски для бизнеса и IT-инфраструктуры. Бесплатные тарифы накладывают жесткие лимиты на количество ссылок, в любой момент могут заблокировать редирект из-за ложных срабатываний антиспам-фильтров или начать показывать пользователям промежуточные страницы с рекламой. Подключение собственного брендированного домена (например, s.company.ru) в коммерческих SaaS-решениях обходится от 35 $ в месяц, а данные об IP-адресах, реферерах и UTM-метках ваших клиентов утекают на зарубежные серверы.

Собственный self-hosted сервис коротких ссылок полностью решает эти проблемы. Развернув Shlink на недорогом VDS, вы получаете неограниченное количество ссылок и кликов, микросекундную скорость отдачи редиректов, удобную панель управления Shlink Web Client, встроенный генератор QR-кодов и детальную аналитику переходов по странам, браузерам и кампаниям на собственном домене.

💡 Почему именно Shlink?

Shlink — это признанный отраслевой стандарт среди open-source сокращателей ссылок (свыше 10 000 звёзд на GitHub). В отличие от устаревших скриптов эпохи PHP 5 (вроде YOURLS), ядро Shlink работает на базе асинхронного рантайма RoadRunner/Swoole. Оно не порождает тяжелые процессы на каждый входящий HTTP-запрос, благодаря чему сервер обрабатывает тысячи перенаправлений в секунду при потреблении всего 40–80 МБ оперативной памяти.

1. Преимущества собственного сервиса коротких ссылок перед публичными SaaS

Для вебмастера, разработчика или владельца интернет-проекта собственный сокращатель ссылок — это не просто эстетика коротких адресов в соцсетях и SMS, а стратегический инструмент контроля трафика:

  • Полный контроль над доменом: ваши ссылки никогда не перестанут открываться из-за смены тарифных планов провайдера или блокировки аккаунта.
  • Безопасность и конфиденциальность: аналитика переходов, включая IP-клиентов и параметры запросов, хранится строго в вашей базе данных на VDS.
  • Мультидоменность: один экземпляр Shlink способен обслуживать десятки разных коротких доменов для разных брендов и проектов.
  • Гибкое управление HTTP-статусами: переключение между редиректами 302/307 (для точной аналитики каждого клика) и 301 (для постоянной передачи веса ссылки в поисковиках).
  • Экономия бюджета: Shlink вместе с базой данных потребляет минимум ресурсов и комфортно работает на базовом тарифе VDS за 200–400 ₽ в месяц.
Критерий Публичные сервисы (Bitly / TinyURL) Собственный Shlink на VDS
Кастомный домен От 35 $/мес за подписку Бесплатно, без ограничений по числу доменов
Лимит переходов Жесткие квоты (от 500 до 1500 кликов на Free) Безлимитно (ограничено только каналом VDS)
Приватность аналитики Данные собираются и монетизируются платформой Изолированная локальная база MariaDB/PostgreSQL
API и автоматизация Rate-limits, платные ключи Полнофункциональный REST API без ограничений

Стек сервиса состоит из трех легковесных компонентов, изолированных в Docker-сети:

  1. Контейнер Shlink (API & Redirect Engine): основное ядро на образе shlinkio/shlink:stable. Принимает входящие HTTP-запросы, резолвит короткий slug в базе данных, регистрирует факт визита (User-Agent, реферер, геолокацию по GeoLite2) и моментально возвращает заголовок Location с целевым URL. Внутри контейнера сервис слушает порт 8080.
  2. Контейнер базы данных (MariaDB или PostgreSQL): хранит таблицы ссылок, доменов, тегов, API-ключей и логов посещений. Для защиты от сетевого сканирования порт СУБД изолируется внутри Docker и не выставляется в глобальный интернет.
  3. Контейнер Shlink Web Client: официальный веб-интерфейс на образе shlinkio/shlink-web-client:stable. Это Single Page Application (SPA), которое связывается с ядром через REST API по защищенному HTTPS-соединению. Слушает внутренний порт 8080.

3. Подготовка домена и настройка DNS-записей

Перед развертыванием контейнеров необходимо делегировать доменные имена на IP-адрес вашего сервера. Для чистоты архитектуры рекомендуется использовать короткий поддомен для редиректов (например, s.mybrand.ru) и отдельный адрес для веб-панели управления (например, links-admin.mybrand.ru).

В панели управления DNS у вашего регистратора или Cloudflare добавьте две ресурсные записи:

s.mybrand.ru.           IN A   203.0.113.10
links-admin.mybrand.ru. IN A   203.0.113.10

Замените 203.0.113.10 на реальный публичный IPv4-адрес вашего VDS. После внесения изменений проверьте распространение ресурсных записей по мировым DNS-узлам с помощью бесплатной утилиты DNS Lookup на SysKit, чтобы убедиться в отсутствии кэширования старых IP-адресов. Также рекомендуем проверить доступность стандартных портов через онлайн сканер портов.

4. Готовый production-манифест Docker Compose и файл .env

Для обеспечения production-безопасности категорически не рекомендуется хардкодить пароли базы данных и API-токены прямо в манифесте docker-compose.yml. Все чувствительные переменные выносятся в изолированный файл .env с ограниченными правами доступа.

Создайте рабочую директорию проекта на сервере:

sudo mkdir -p /opt/shlink && cd /opt/shlink

Сгенерируйте криптостойкие пароли с помощью генератора безопасных паролей SysKit либо встроенной утилиты OpenSSL:

openssl rand -hex 16

Создайте файл /opt/shlink/.env со следующим содержимым:

# Домен сервиса коротких ссылок
DEFAULT_DOMAIN=s.mybrand.ru

# Учетные данные MariaDB
DB_NAME=shlink
DB_USER=shlink_user
DB_PASSWORD=generated_super_secret_db_pass_123
DB_ROOT_PASSWORD=generated_root_super_pass_456

# Инициализирующий API-ключ для первого входа
INITIAL_API_KEY=shlink_secret_init_key_789abc

# Бесплатный лицензионный ключ MaxMind для геолокации кликов
GEOLITE_LICENSE_KEY=your_maxmind_license_key_here

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

sudo chmod 600 /opt/shlink/.env

Теперь создайте файл манифеста docker-compose.yml, использующий эти переменные и обязательную директиву TRUSTED_PROXIES:

services:
  shlink_db:
    image: mariadb:11
    container_name: shlink_db
    restart: unless-stopped
    mem_limit: 512m
    cpus: 0.50
    environment:
      MARIADB_DATABASE: ${DB_NAME}
      MARIADB_USER: ${DB_USER}
      MARIADB_PASSWORD: ${DB_PASSWORD}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - shlink_db_data:/var/lib/mysql
    networks:
      - shlink_net

  shlink:
    image: shlinkio/shlink:stable
    container_name: shlink_app
    restart: unless-stopped
    mem_limit: 384m
    cpus: 0.75
    depends_on:
      - shlink_db
    environment:
      DEFAULT_DOMAIN: ${DEFAULT_DOMAIN}
      IS_HTTPS_ENABLED: "true"
      DB_DRIVER: maria
      DB_HOST: shlink_db
      DB_NAME: ${DB_NAME}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      INITIAL_API_KEY: ${INITIAL_API_KEY}
      TRUSTED_PROXIES: "1"
      REDIRECT_STATUS_CODE: "302"
      REDIRECT_CACHE_LIFETIME: "30"
      TIMEZONE: "Europe/Moscow"
      GEOLITE_LICENSE_KEY: ${GEOLITE_LICENSE_KEY:-}
    ports:
      - "127.0.0.1:8080:8080"
    networks:
      - shlink_net

  shlink_web:
    image: shlinkio/shlink-web-client:stable
    container_name: shlink_web
    restart: unless-stopped
    mem_limit: 128m
    cpus: 0.25
    ports:
      - "127.0.0.1:8081:8080"
    networks:
      - shlink_net

volumes:
  shlink_db_data:

networks:
  shlink_net:
    driver: bridge

⚠️ Важность директивы TRUSTED_PROXIES и изоляции портов

В Shlink v4 для повышения безопасности отключено автоматическое доверие заголовкам обратного прокси. Параметр TRUSTED_PROXIES: "1" сообщает ядру, что прямо перед ним находится ровно один доверенный Reverse Proxy (Caddy или Nginx). Без этой настройки Shlink проигнорирует заголовок X-Forwarded-For и запишет IP-адрес локального прокси 127.0.0.1 вместо реального IP посетителя, полностью сломав геолокацию кликов.

Кроме того, порты привязаны строго к 127.0.0.1, что защищает от обхода UFW через Docker iptables. Подробнее о сетевой защите читайте в руководстве по защите портов Docker от обхода UFW.

5. Настройка обратного прокси (Caddy 2 и Nginx) с правильными заголовками и SSL

Ядро Shlink и веб-панель не должны обрабатывать TLS напрямую. Терминацию SSL, сжатие трафика и проброс реальных IP-адресов клиентов выполняет внешний веб-сервер. Ниже представлены два проверенных варианта настройки.

Вариант 1. Современный веб-сервер Caddy 2 (Рекомендуется)

Веб-сервер Caddy автоматически получает, верифицирует и продлевает SSL-сертификаты Let's Encrypt без использования Certbot. Для полной совместимости с асинхронным ядром Shlink явно пробросим заголовки X-Real-IP и X-Forwarded-Host. Добавьте в файл /etc/caddy/Caddyfile следующую конфигурацию:

s.mybrand.ru {
    encode zstd gzip
    reverse_proxy 127.0.0.1:8080 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Host {host}
    }
}

links-admin.mybrand.ru {
    encode zstd gzip
    reverse_proxy 127.0.0.1:8081
}

Перечитайте конфигурацию: sudo systemctl reload caddy. Подробнее об особенностях архитектуры Caddy читайте в руководстве по настройке веб-сервера Caddy 2 с авто-SSL для Docker.

Вариант 2. Классический Nginx с Certbot

Если на вашем сервере используется Nginx, создайте виртуальный хост в каталоге /etc/nginx/sites-available/. Для сервиса редиректов s.mybrand.ru обязательно передайте полный комплект прокси-заголовков, включая X-Forwarded-Host для поддержки мультидоменности:

server {
    listen 80;
    server_name s.mybrand.ru;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_read_timeout 60s;
    }
}

Аналогично создается блок для админ-панели links-admin.mybrand.ru с проксированием на http://127.0.0.1:8081. После проверки синтаксиса (sudo nginx -t) и перезапуска службы выпустите бесплатные сертификаты с автоматической настройкой HTTPS-редиректа:

sudo certbot --nginx --redirect -d s.mybrand.ru -d links-admin.mybrand.ru

После активации проверьте корректность цепочки TLS-сертификатов через онлайн SSL-чекер SysKit. Если требуется усилить безопасность заголовков, используйте готовый pSEO-шаблон конфигурации безопасных заголовков Nginx.

6. Запуск сервиса, настройка GeoLite2 и подключение админки

Чтобы Shlink мог строить интерактивную карту кликов с детализацией по странам и городам, требуется бесплатная база данных MaxMind GeoLite2:

  1. Зарегистрируйтесь на официальном сайте MaxMind GeoLite2.
  2. В личном кабинете перейдите в раздел Manage License Keys → Generate new license key.
  3. Скопируйте ключ и вставьте его в значение переменной GEOLITE_LICENSE_KEY в файле /opt/shlink/.env. При первом запуске Shlink автоматически скачает и развернет актуальную базу городов.

Запустите стек Docker Compose в фоновом режиме:

docker compose up -d

Проверьте статус контейнеров: docker compose ps. Все три службы (shlink_app, shlink_db, shlink_web) должны иметь статус Up. Если вам понадобятся дополнительные API-ключи (например, с ограничением только на определенный домен или автора), их можно выпустить через встроенную CLI-утилиту контейнера:

docker compose exec shlink bin/cli api-key:generate

Консоль вернет сгенерированный токен. Теперь откройте в браузере адрес вашей панели управления https://links-admin.mybrand.ru:

  1. Нажмите кнопку Add a server.
  2. В поле Name укажите произвольное имя (например, Production Shlink).
  3. В поле URL введите полный адрес ядра с HTTPS: https://s.mybrand.ru.
  4. В поле API Key вставьте значение из переменной INITIAL_API_KEY или сгенерированный токен.
  5. Нажмите Create server.

Веб-клиент мгновенно подключится к API ядра и откроет дашборд. Вся конфигурация сохраняется в локальном хранилище браузера (LocalStorage), поэтому ваши ключи не передаются третьим лицам.

7. Коды редиректа 301, 302, 307, защита аналитики и нюансы CDN

Один из самых частых вопросов при настройке собственного сокращателя — какой код HTTP-редиректа выбрать. Разница между ними критична для учета статистики:

  • 301 (Moved Permanently): постоянный редирект. Браузер пользователя сохраняет соответствие «короткая ссылка → целевая страница» в своем локальном кэше. При повторных переходах браузер даже не делает запрос к серверу Shlink, а сразу открывает конечный адрес. В результате повторные клики одного и того же посетителя не регистрируются в аналитике, а счетчик посещений занижается.
  • 302 (Found) / 307 (Temporary Redirect): временное перенаправление. Браузер обязан обращаться к серверу Shlink при каждом клике. Сервер фиксирует дату, User-Agent, страну и UTM-метки, после чего моментально направляет посетителя дальше. Именно код 302 задан по умолчанию в директиве REDIRECT_STATUS_CODE.

⚠️ Осторожно с кэшированием 302 на уровне CDN (Cloudflare)

Хотя браузеры не кэшируют редиректы 302/307 на постоянной основе, промежуточные прокси-сети и CDN (например, Cloudflare с активным правилом Cache Everything) могут закэшировать 302-ответ прямо на граничных серверах (Edge Cache). Если перед Shlink настроен Cloudflare, обязательно создайте Page Rule или Cache Rule для поддомена s.mybrand.ru/* с параметром Cache Level: Bypass, чтобы каждый клик доходил до вашего VDS.

В Shlink можно тонко настраивать каждую ссылку прямо в веб-интерфейсе:

  • Кастомный slug: создание красивых адресов вида s.mybrand.ru/autumn-sale вместо случайных символов.
  • Ограничение по времени и кликам: параметры validUntil и maxVisits позволяют автоматически отключить редирект после завершения маркетинговой акции.
  • Парольная защита: ссылка запрашивает пароль у посетителя перед выполнением перенаправления.
  • Встроенный QR-код: автоматическая генерация векторных SVG и растровых PNG QR-кодов для полиграфии с возможностью настройки цвета и размера.

8. Миграция существующих ссылок: встроенный импорт из Bitly, YOURLS и CSV

Если вы ранее использовали другие сервисы или вели базу ссылок в таблицах Excel/Google Sheets, переносить их вручную не придется. Shlink содержит встроенный интерактивный мастер импорта short-url:import, который поддерживает автоматическую миграцию ссылок и истории переходов:

  • Bitly: подключение через Generic Access Token из личного кабинета Bitly. Shlink выгружает все ссылки, созданные под вашим аккаунтом.
  • YOURLS: прямое подключение к базе данных старого сервера YOURLS по реквизитам доступа.
  • CSV: универсальный импорт из текстового файла со списком соответствий longUrl, shortCode, title и tags.
  • Kutt: перенос из популярного Node.js-сокращателя Kutt.

Для запуска миграции выполните интерактивную команду в консоли контейнера:

docker compose exec shlink bin/cli short-url:import

CLI-мастер предложит выбрать источник стрелками на клавиатуре, запросит API-токен или путь к файлу CSV, после чего безопасно импортирует ссылки в базу MariaDB. Процедура идемпотентна: повторный запуск импорта не создаст дубликатов уже перенесенных ссылок.

9. Проверка работы редиректов и траблшутинг

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

curl -ILs https://s.mybrand.ru/test | grep -E "HTTP|location"

В выводе должен присутствовать корректный статус перенаправления и целевой заголовок location:

HTTP/2 302
location: https://mybrand.ru/landing-page/?utm_source=shortlink

Для комплексного аудита цепочки перенаправлений воспользуйтесь онлайн-инструментом проверки редиректов SysKit. Он визуализирует всю цепочку прыжков, помогает выявить скрытые задержки и предотвратить циклические перенаправления. Подробнее о механике циклов читайте в статье о поиске причин циклических редиректов ERR_TOO_MANY_REDIRECTS.

Рекомендуемые VDS-провайдеры

Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с

Выбор редакции
⚡

Timeweb Cloud — Быстрые NVMe VDS

Идеальная инфраструктура для self-hosted сервисов и Docker Compose: порты 80/443 открыты, мгновенный аптайм и сеть до 1 Гбит/с.

Конфигурация
1 vCPU 3.3 ГГц • 1 ГБ RAM • 15 ГБ NVMe • Трафик без лимита
Премиум Tier III
🔷

Selectel — Надежные облачные серверы

Дата-центры уровня Tier III в Москве и Санкт-Петербурге с почасовой оплатой, приватными подсетями и защитой от DDoS.

Конфигурация
1 vCPU • 2 ГБ RAM • 30 ГБ NVMe • Канал 100 Мбит/с
Для вебмастеров
🟠

Beget — Простота и стабильность VDS

Удобная панель управления сервером, автоматические бэкапы и предустановленный стек Docker Compose в один клик.

Конфигурация
1 vCPU 3.0 ГГц • 1 ГБ RAM • 20 ГБ NVMe • Трафик без лимита

Разверните свой сервис коротких ссылок за 15 минут

Для стабильной работы Shlink, MariaDB и веб-панели с гарантированным каналом связи выберите быстрый облачный сервер на скоростных NVMe-накопителях у надежных провайдеров.

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

Можно ли привязать несколько разных коротких доменов к одному инстансу Shlink?
Да, Shlink нативно поддерживает мультидоменность. Главный домен указывается в переменной DEFAULT_DOMAIN, а любые дополнительные домены можно добавлять через веб-панель управления или API. Главное — направить DNS-записи всех доменов на IP вашего сервера и настроить проксирование в Caddy или Nginx.
Почему Shlink по умолчанию использует редирект 302 вместо 301?
Код 302 (Found) предотвращает агрессивное кэширование ссылки на стороне браузера пользователя. При коде 301 браузер сохраняет конечный URL локально и при повторных переходах вообще не обращается к серверу сокращателя, из-за чего статистика кликов сильно занижается. Временный редирект 302 гарантирует точный учет каждого визита.
Сколько оперативной памяти требуется для Shlink, MariaDB и веб-панели на сервере?
Стек крайне экономичен: ядро Shlink на Swoole/RoadRunner потребляет около 40–80 МБ RAM, веб-клиент — около 20–30 МБ, а база данных MariaDB при базовой нагрузке — 150–250 МБ. Таким образом, весь комплекс комфортно работает на самом доступном VDS с 1 ГБ оперативной памяти.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи