Свой Speedtest с HTTPS на Ubuntu (LibreSpeed + nginx + Let's Encrypt)
Инструкция поднимает собственный веб-спидтест на сервере Ubuntu с валидным HTTPS-сертификатом.
Работает на любом порту, в том числе когда порты 80 и 443 заняты (например, роутером MikroTik), потому что сертификат выпускается через DNS-challenge (Cloudflare) и входящие порты для этого не нужны.
- Репозиторий speedtest: https://github.com/librespeed/speedtest
- Сертификаты: acme.sh + Let's Encrypt
- Прокси для HTTPS: nginx
Как это работает
Браузер ──HTTPS:8080──▶ nginx (TLS) ──HTTP──▶ 127.0.0.1:8081 ──▶ контейнер LibreSpeed
- Контейнер слушает только
127.0.0.1:8081и снаружи недоступен. - nginx принимает HTTPS на внешнем порту (
8080в примере) и проксирует на контейнер. - Сертификат выпускается и продлевается автоматически через DNS-запись в Cloudflare.
Что понадобится
- Сервер Ubuntu 22.04 / 24.04 с root-доступом (хватает 2 CPU и 1–2 ГБ RAM для канала до ~1 Гбит/с).
- Домен, DNS которого управляется в Cloudflare, и A-запись
speedtest.example.com→ публичный IP сервера (в Cloudflare поставьте режим DNS only, серое облако: прокси Cloudflare не пропускает такие порты и ограничивает размер загрузки). - Если сервер за роутером: проброс выбранного TCP-порта на сервер (см. раздел «Роутер»).
Переменные
Дальше везде используются эти значения. Задайте свои:
| Что | Пример | Комментарий |
|---|---|---|
| Домен | speedtest.example.com |
должен быть в Cloudflare |
| Внешний порт HTTPS | 8080 |
любой свободный |
| Внутренний порт контейнера | 8081 |
должен отличаться от внешнего |
1. Подготовка: чистим старые прокси (если есть)
Нужно, только если на сервере раньше стояли nginx или Caddy. purge удаляет их конфиги, сохраните нужное заранее.
sudo -i
systemctl disable --now nginx caddy 2>/dev/null
apt purge -y 'nginx*' caddy
apt autoremove -y
rm -rf /etc/nginx /etc/caddy /var/lib/caddy
Проверьте, что нужные порты свободны:
ss -tlnp | grep -E ':8080 |:8081 '
Вывод должен быть пустым.
2. Токен Cloudflare
В Cloudflare: My Profile → API Tokens → Create Token → Create Custom Token.
- Token name: любое, например
acme-dns. - Permission policies (область «Zone», можно ограничить своим доменом в выпадающем списке «All Domains»):
DNS & Zones → DNS → EditDNS & Zones → Zone → Read- Token expiration: No expiration (иначе автопродление сертификата перестанет работать).
- Нажмите Review token → Create Token и скопируйте токен, он показывается один раз.
Токен нигде не публикуйте и не отправляйте в чаты, вводите только на сервере.
3. Установка LibreSpeed в Docker
apt update && apt install -y docker.io docker-compose-v2
systemctl enable --now docker
mkdir -p ~/speedtest && cd ~/speedtest
cat > docker-compose.yml <<'EOF'
services:
speedtest:
container_name: speedtest
image: ghcr.io/librespeed/speedtest:latest
restart: always
environment:
MODE: standalone
TITLE: "My Speedtest"
TELEMETRY: "false"
ports:
- "127.0.0.1:8081:8080"
EOF
docker compose up -d
curl -sI http://127.0.0.1:8081 | head -1
Последняя команда должна вывести HTTP/1.1 200 OK.
4. Установка nginx
apt install -y nginx
rm -f /etc/nginx/sites-enabled/default
mkdir -p /etc/nginx/ssl
5. Получение сертификата (DNS-challenge)
curl https://get.acme.sh | sh -s email=you@example.com
source ~/.bashrc
acme.sh --set-default-ca --server letsencrypt
Задайте домен и токен и выпустите сертификат:
export DOMAIN="speedtest.example.com"
export CF_Token="ВАШ_ТОКЕН"
acme.sh --issue --dns dns_cf -d "$DOMAIN"
Установка сертификата для nginx. После каждого продления nginx перезагрузится сам:
acme.sh --install-cert -d "$DOMAIN" \
--key-file /etc/nginx/ssl/speedtest.key \
--fullchain-file /etc/nginx/ssl/speedtest.crt \
--reloadcmd "systemctl reload nginx"
Автопродление настроено через cron при установке acme.sh, проверить: acme.sh --list.
6. HTTPS-конфиг nginx
cat > /etc/nginx/conf.d/speedtest.conf <<'EOF'
server {
listen 8080 ssl;
server_name SPEEDTEST_DOMAIN;
ssl_certificate /etc/nginx/ssl/speedtest.crt;
ssl_certificate_key /etc/nginx/ssl/speedtest.key;
ssl_protocols TLSv1.2 TLSv1.3;
# для теста upload
client_max_body_size 0;
proxy_request_buffering off;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_buffering off;
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;
}
}
EOF
sed -i "s/SPEEDTEST_DOMAIN/$DOMAIN/" /etc/nginx/conf.d/speedtest.conf
nginx -t && systemctl enable --now nginx && systemctl restart nginx
Другие порты: замените 8080 в listen на нужный внешний, а 8081 в proxy_pass на внутренний порт контейнера (из docker-compose.yml).
7. Файрвол сервера
ufw allow 8080/tcp
8. Проверка
Локально на сервере:
ss -tlnp | grep -E ':8080 |:8081 '
curl -I --resolve "$DOMAIN:8080:127.0.0.1" "https://$DOMAIN:8080"
Ожидаемо: 127.0.0.1:8081 у docker-proxy, 0.0.0.0:8080 у nginx, ответ HTTP/1.1 200 OK.
Снаружи откройте в браузере https://speedtest.example.com:8080/. Замок должен быть зелёным, предупреждений нет.
Роутер (если сервер за NAT)
Нужен проброс выбранного внешнего порта на сервер. Пример для MikroTik, подставьте IP сервера в локальной сети:
/ip firewall nat add chain=dstnat protocol=tcp dst-port=8080 in-interface-list=WAN action=dst-nat to-addresses=192.168.X.X to-ports=8080 comment="speedtest https"
/ip firewall filter add chain=forward protocol=tcp dst-port=8080 dst-address=192.168.X.X action=accept place-before=0 comment="speedtest https"
Убедитесь, что этот порт не занят самим роутером (WebFig, API и т.п.).
Частые проблемы
| Симптом | Причина и решение |
|---|---|
curl: (35) wrong version number |
На порту отвечает не nginx, а контейнер напрямую (HTTP). Проверьте ports: в docker-compose.yml: должно быть 127.0.0.1:8081:8080. Затем docker compose up -d --force-recreate и systemctl restart nginx. |
bind() to 0.0.0.0:8080 failed |
Порт занят другим процессом: ss -tlnp | grep :8080. |
| acme.sh не находит зону / ошибка авторизации | Проверьте права токена (DNS Edit и Zone Read) и что домен действительно в Cloudflare. |
| Сертификат не продлевается | Токен истёк или удалён. Создайте новый, выполните export CF_Token=... и acme.sh --renew -d "$DOMAIN" --force. |
| Скорость меньше ожидаемой | Проверьте загрузку CPU (top -d 1) во время теста, канал сервера и роутер (NAT и conntrack на MikroTik). |
| Нужен HTTPS «без домена» | Let's Encrypt не выдаёт сертификаты на голый IP. Можно сделать самоподписанный сертификат через openssl, но браузер покажет предупреждение. |
Обновление и удаление
Обновить LibreSpeed:
cd ~/speedtest && docker compose pull && docker compose up -d
Удалить всё:
cd ~/speedtest && docker compose down && cd ~ && rm -rf ~/speedtest
apt purge -y 'nginx*' && rm -rf /etc/nginx
acme.sh --uninstall
И удалить A-запись и токен в Cloudflare.