yt-dlp-gui/deploy/nginx-yt.zapret.moe.conf
codex-pve 8ab4f6904e Лента пропадала при перезагрузке: разделены лимиты, честные ошибки
Единый лимит 30r/m на весь /api/ был рассчитан как защита от абуза, но
бил по обычной навигации: страница статистики делает два запроса на
загрузку плюс автообновление, поэтому уже 4-5 перезагрузок подряд
упирались в лимит и nginx отдавал 503.

Лимиты разделены по цене операции: разбор ссылки и запуск загрузки
(дёргают сеть и yt-dlp) остаются на 30r/m, а чтение статистики, ленты и
статусов задач получает 240r/m. Проверено: 44 запроса чтения подряд
проходят, /api/info по-прежнему отсекается на 429.

Ответ при превышении сменён с 503 на 429: это точный код для «слишком
часто», и он не приходит HTML-страницей, на которой ломался разбор JSON.
Лимит одновременных соединений поднят с 8 до 24 — браузер сам открывает
до шести, а SSE держит своё на всю загрузку.

Фронтенд больше не выдаёт сбой за пустоту. Раньше неудачный запрос ронял
разбор JSON, и страница показывала «Пока пусто» — то есть утверждала, что
данных нет, хотя их просто не отдали. Теперь показывается причина.

Также: срок жизни файла поднят с часа до двух (за это время файл в 2 ГБ
успевает скачаться даже на 2,3 Мбит/с), и устранена утечка в rate-limiter
— ключи словаря _hits не удалялись никогда, поэтому перебор адресов
медленно выедал память процесса.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwY2Cg54RK7WZdvMaLV5Di
2026-09-21 17:18:08 +03:00

136 lines
5.6 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# yt-grab — yt.zapret.moe
# Устанавливается в /etc/nginx/sites-available/ и линкуется в sites-enabled/.
# Ограничение частоты на уровне nginx (вдобавок к лимитам в приложении).
#
# Два разных лимита, потому что операции несопоставимы по цене:
# * heavy — разбор ссылки и запуск загрузки: дёргают сеть и yt-dlp;
# * read — чтение статистики, ленты и статусов задач: почти бесплатно.
# Единый строгий лимит ломал обычную навигацию: страница статистики делает
# два запроса на загрузку плюс автообновление, и несколько перезагрузок
# подряд выбивали 503 на ровном месте.
limit_req_zone $binary_remote_addr zone=ytgrab_heavy:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=ytgrab_read:10m rate=240r/m;
limit_conn_zone $binary_remote_addr zone=ytgrab_conn:10m;
# Отдаём 429 вместо 503: это честный код для «слишком часто», и клиент
# может его распознать (503 приходил HTML-страницей и ломал разбор JSON).
limit_req_status 429;
limit_conn_status 429;
server {
listen 80;
listen [::]:80;
server_name yt.zapret.moe;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
}
location / {
return 301 https://yt.zapret.moe$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name yt.zapret.moe;
ssl_certificate /etc/letsencrypt/live/yt.zapret.moe/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yt.zapret.moe/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Frame-Options "DENY" always;
charset utf-8;
# Загрузок от клиента нет — тело запроса всегда маленький JSON.
client_max_body_size 64k;
# Одновременные соединения с одного IP. Браузер сам открывает до шести,
# плюс SSE держит своё открытым на всю загрузку, поэтому 8 было тесно.
limit_conn ytgrab_conn 24;
# --- Поток прогресса (SSE): буферизация должна быть выключена ---
location ~ ^/api/tasks/[^/]+/progress$ {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_buffering off;
proxy_cache off;
gzip off;
chunked_transfer_encoding on;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
}
# --- Отдача готового файла: большие тела, докачка по Range ---
location ~ ^/api/tasks/[^/]+/file$ {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
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_buffering off;
proxy_max_temp_file_size 0;
proxy_read_timeout 1h;
}
# --- Дорогие операции: разбор ссылки и запуск загрузки ---
location = /api/info {
limit_req zone=ytgrab_heavy burst=10 nodelay;
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
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_read_timeout 180s;
}
location = /api/downloads {
limit_req zone=ytgrab_heavy burst=10 nodelay;
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
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_read_timeout 180s;
}
# --- Дешёвое чтение: статистика, лента, статусы задач ---
location /api/ {
limit_req zone=ytgrab_read burst=60 nodelay;
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
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_read_timeout 180s;
}
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
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;
}
}