MAATRIX / Блог / PrivateBin на сервере: частые ошибки и решения

PrivateBin на сервере: частые ошибки и решения

MAATRIX

PrivateBin выглядит как простой PHP-скрипт, но ломается он в местах, которые не видны из документации: права на каталог данных, заголовки CSP за реверс-прокси, HTTPS как обязательное условие для самого шифрования. Ниже — конкретные ошибки, с которыми сталкиваются при установке и эксплуатации PrivateBin на своём сервере, и рабочие решения для каждой.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Ошибки PHP-расширений при установке

Первое, на чём спотыкается свежая установка — нехватка PHP-расширений. PrivateBin проверяет их при заходе на страницу и честно пишет, чего не хватает: PrivateBin requires PHP-JSON extension to work или похожие сообщения про mbstring, gettext, zlib, ctype. На Ubuntu/Debian с PHP-FPM это лечится одной командой (номер версии PHP подставьте свой):

sudo apt update
sudo apt install php8.3-fpm php8.3-json php8.3-mbstring php8.3-gd php8.3-curl php8.3-zip php8.3-sqlite3
sudo systemctl restart php8.3-fpm

Обратите внимание на php8.3-json — в некоторых сборках PHP 8+ JSON уже встроен в ядро, тогда apt скажет, что пакет не найден или уже установлен виртуально — это нормально. gd нужен для капчи на основе изображений в настройках антиспама, sqlite3 — если храните данные в SQLite, а не в файлах напрямую.

После установки проверьте, что PHP-FPM и nginx смотрят на одну и ту же версию PHP — частая ситуация, когда на сервере стоит несколько версий сразу, и php -v показывает 8.3, а конфиг nginx указывает на сокет 8.1. Проверить активные модули конкретной версии:

php8.3 -m | grep -Ei 'json|mbstring|gettext|zlib|ctype'

Если модуль есть в списке, а ошибка всё равно всплывает — почти всегда причина в том, что перезапустили не тот сервис или изменения не подхватились, потому что редактировали php.ini не той версии.

«Возникла ошибка на стороне сервера»: права на data и conf.php

Самая частая рантайм-ошибка PrivateBin — общая фраза Server error или An error occurred, please try again later, без деталей. За ней почти всегда прячется одна из двух причин: PHP-процесс не может писать в каталог data/, где хранятся зашифрованные вставки, либо не может создать cfg/conf.php при первом запуске (если вы не положили конфиг заранее).

Проверьте владельца каталога и права на запись:

ls -la /var/www/privatebin/data
sudo chown -R www-data:www-data /var/www/privatebin/data /var/www/privatebin/cfg
sudo chmod 770 /var/www/privatebin/data

Пользователя www-data замените на реального пользователя, от которого работает ваш пул PHP-FPM — обычно он указан в /etc/php/8.3/fpm/pool.d/www.conf, в директивах user и group. Если PrivateBin развёрнут в Docker (официальный образ privatebin/nginx-fpm-alpine), права нужно выставлять на volume, смонтированный в /srv/app/data внутри контейнера — если volume примонтирован с хоста с другим UID, запись обрывается молча, а в логах та же общая Server error.

Реальные детали ошибки видно только в логах PHP-FPM или в error_log PHP:

sudo tail -50 /var/log/php8.3-fpm.log
sudo journalctl -u php8.3-fpm -n 50

Там же обычно находится и вторая частая причина — синтаксическая ошибка в cfg/conf.php после ручного редактирования (пропущенная точка с запятой, лишняя кавычка). PrivateBin в этом случае просто падает в 500 без объяснений на странице, поэтому лог — единственный источник правды.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Шифрование не работает без HTTPS

PrivateBin шифрует и расшифровывает вставки прямо в браузере через Web Crypto API — сервер вообще не видит содержимое в открытом виде, это и есть весь смысл сервиса. Но у Web Crypto API есть жёсткое требование: он работает только в «безопасном контексте» — на HTTPS или на localhost. Если открыть PrivateBin по обычному HTTP на боевом домене, кнопка отправки вставки либо не сработает, либо в консоли браузера вы увидите ошибку вида crypto.subtle is undefined или Cannot read properties of undefined (reading 'importKey').

Это не баг конфигурации PrivateBin — это ограничение самого браузера, обойти его нельзя, и правильное решение только одно: включить HTTPS. Проще всего через Let's Encrypt и certbot:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d paste.вашдомен.ru

Если сертификат уже был выпущен, но перестал обновляться и истёк — это отдельная и тоже частая проблема, разобранная в статье про SSL-сертификат, который не обновился. Пока сертификат просрочен, браузер либо блокирует страницу предупреждением, либо (в зависимости от настроек) режет её до state, где Web Crypto API снова недоступен из-за не полностью безопасного соединения.

Отдельно проверьте, что весь путь до PrivateBin действительно HTTPS, а не только внешний домен: если между nginx и PHP-FPM или между CDN и вашим сервером трафик идёт по HTTP, а сам браузер видит https:// в адресной строке — для Web Crypto API это уже не важно, он смотрит на протокол именно во вкладке браузера. Проблема возникает, когда сайт вообще не переезжает на HTTPS и остаётся на плейне для теста «пока не готов сертификат» — в таком виде PrivateBin для реального использования непригоден.

CSP-заголовки и белый экран за nginx-прокси

PrivateBin отдаёт строгий заголовок Content-Security-Policy с одноразовым nonce для инлайн-скриптов, и генерирует его сам PHP на каждый запрос заново. Если перед PrivateBin стоит nginx как реверс-прокси и в его конфиге уже есть свои директивы add_header, вы можете получить страницу, которая грузится, но остаётся пустой или выдаёт в консоли Refused to execute inline script because it violates the following Content Security Policy directive.

Причина в особенности nginx: если add_header задан и в server, и в location, срабатывает только ближайший к запросу блок, а заголовки из server полностью игнорируются — они не складываются. Если ваш location / { add_header X-Frame-Options ...; } случайно перекрыл исходный CSP-заголовок PrivateBin своим, nonce из HTML-разметки перестаёт совпадать с тем, что браузер видит в заголовке ответа — скрипт блокируется.

Практический совет: не трогайте заголовок Content-Security-Policy в nginx-конфиге для PrivateBin вообще, дайте его выставлять самому приложению. Если вам всё же нужны другие заголовки (X-Frame-Options, Referrer-Policy), добавляйте их через proxy_pass без собственного CSP, либо явно копируйте существующий Content-Security-Policy через add_header ... always в каждом location-блоке, где он нужен, помня про правило «ближайший блок побеждает». Базовый рабочий фрагмент конфига:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Заголовок X-Forwarded-Proto здесь обязателен: без него PHP за прокси может решить, что соединение идёт по HTTP, даже если снаружи всё HTTPS, и часть логики (в том числе флаги cookie Secure) отработает неправильно. Общие грабли reverse-proxy конфигурации для PHP-приложений разобраны подробнее в статье про nginx как реверс-прокси — большинство описанных там причин актуальны и для PrivateBin.

413 и лимиты на размер вставки

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

Где ограничениеДирективаТипичное значение по умолчанию
nginxclient_max_body_size1m
PHPpost_max_size8M
PHPupload_max_filesize2M
PrivateBinsizelimit в cfg/conf.php10485760 (10 МБ)

Если nginx лимит меньше, чем нужно, браузер получает 413 Request Entity Too Large прямо на уровне HTTP, ещё до того как запрос дойдёт до PHP. Поднимите лимит в конфиге сайта:

server {
    client_max_body_size 20m;
    ...
}

Если nginx пропустил запрос, а PrivateBin всё равно отклоняет вставку с ошибкой про превышение размера — смотрите секцию [main] в cfg/conf.php, параметр sizelimit задаётся в байтах, и его стоит сверить с post_max_size из php.ini — оба значения должны быть согласованы, иначе более строгий из них станет реальным лимитом. Не выставляйте лимиты слишком щедрыми без причины: PrivateBin хранит все вставки на диске в зашифрованном виде до истечения срока жизни, и без ограничения размера кто-то может быстро забить диск сервера — особенно если инстанс открыт публично без авторизации.

Очистка истёкших записей и переход на СУБД

По умолчанию PrivateBin удаляет истёкшие вставки не по расписанию, а «между делом» — при каждом обращении к странице с некоторой вероятностью запускается сборщик мусора (garbage collector), который проверяет data/ и чистит записи с истёкшим сроком жизни. На небольшом личном инстансе это работает незаметно. На сервере с заметным трафиком это означает лишнюю дисковую нагрузку прямо во время обработки случайных пользовательских запросов, что иногда проявляется как непредсказуемые задержки.

Правильный подход — отключить встроенный шанс запуска GC и вызывать очистку по расписанию через cron:

// cfg/conf.php, секция [purge]
[purge]
limit = 0

И отдельное задание cron, которое дергает purge через CLI-обвязку или curl с параметром, в зависимости от версии PrivateBin — актуальный способ запуска описан в README конкретного релиза, который у вас установлен, потому что механизм CLI-очистки менялся между версиями. Общие принципы настройки регулярных задач и типичные ошибки cron — синтаксис расписания, окружение PATH, права пользователя — разобраны в статье про cron-задачи на сервере.

Если инстанс растёт и файловое хранилище на большом числе вставок начинает тормозить на списках и очистке, PrivateBin умеет работать с базой данных вместо файлов — MySQL, PostgreSQL или SQLite через PDO. Переезд делается правкой секции [model] в cfg/conf.php:

[model]
class = Database
[model_options]
dsn = "mysql:host=127.0.0.1;dbname=privatebin;charset=utf8mb4"
tbl = "privatebin_"
usr = "privatebin_user"
pwd = "надёжный_пароль"

Частая ошибка на этом шаге — забыть установить PHP-расширение pdo_mysql (или pdo_pgsql), тогда при попытке сохранить первую вставку появляется could not find driver. И вторая — не создать заранее саму базу и пользователя с правами на неё: PrivateBin не создаёт базу данных сам, только таблицы внутри уже существующей. Если после переезда на СУБД старые вставки «пропали» — это ожидаемо, они остались в data/, но приложение теперь читает только из базы; полноценная миграция данных есть не для всех версий, поэтому выбор хранилища лучше делать заранее, а не менять на живом инстансе с историей.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли запустить PrivateBin без HTTPS для внутреннего теста?

Технически страница откроется, но кнопка создания зашифрованной вставки не будет работать в браузере из-за требований Web Crypto API к безопасному контексту. Исключение — доступ строго через localhost, там браузеры считают контекст безопасным даже без сертификата.

Почему после смены домена или порта перестали открываться старые ссылки на вставки?

Ключ расшифровки лежит в самой ссылке после #, и на сервер эта часть URL никогда не отправляется — сервер её физически не видит. Если ссылка не открывается, чаще всего проблема не в ключе, а в том, что сама вставка истекла и была удалена сборщиком мусора, либо изменился путь (basePath) в конфиге, из-за чего ссылки стали вести не туда.

Нужен ли антивирус или проверка вложений, если через PrivateBin можно прикреплять файлы?

Сервер не видит содержимое — оно зашифровано ещё в браузере до отправки, поэтому серверное сканирование контента невозможно в принципе, это осознанный компромисс zero-knowledge подхода. Ограничивайте риск размером вставки, сроком жизни по умолчанию и, если инстанс публичный, добавьте капчу и авторизацию на создание записей.

Как защитить публичный PrivateBin от спам-ботов, которые заливают вставки пачками?

Включите капчу в cfg/conf.php (секция [traffic] и [main] — captcha, лимит по IP) и рассмотрите ограничение частоты запросов на уровне nginx (limit_req) вместе с fail2ban — как настроить правила именно под nginx, разобрано в статье про fail2ban для nginx.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →