# 10 · Неполадки

Каждый пункт: симптом → причина → действие. Всё проверено на живой установке.

## Панель не запускается

| Симптом | Причина | Действие |
|---|---|---|
| `Fornex Control: no configuration found` | нет `fornex.env`, нет `.env`, нет `FORNEX_ENV` и нет `FORNEX_API_KEY` в окружении | положить файл настроек уровнем выше приложения |
| `FORNEX_API_KEY is not set in .env` | файл найден, ключ пуст | вписать ключ из кабинета Fornex |
| 500 без тела, в журнале «permission denied» | `var/` не доступен на запись пользователю PHP | `chown -R www-data:www-data var` |
| Вход отвечает `bad_credentials` при верном пароле | `ADMIN_PASSWORD_HASH` пуст либо вписан с переносом строки | пересоздать хэш и вставить без переноса |

## Интерфейс после обновления

| Симптом | Причина | Действие |
|---|---|---|
| Старая вёрстка, старый код, старый текст | `app.js` взят из кеша браузера | ссылки содержат `?v=<версия>`; поднять `APP_VERSION`, и адрес изменится |
| Белая страница, в консоли 404 на `assets/…` | `APP_BASE` не совпадает с подкаталогом | `APP_BASE=/fornex` либо пусто для корня |
| Окно появляется в углу и закрывается от любого клика | диалог был соседом подложки, а не её ребёнком | исправлено в 1.1.0: диалог внутри `.modal-backdrop`, закрытие по `mousedown` вне окна |
| Кнопка не нажимается и ничего не объясняет | `disabled` не умеет объяснять | кнопка `aria-disabled`: клик сообщает, чего не хватает |

## Зона и записи

| Симптом | Причина | Действие |
|---|---|---|
| `403 You do not have permission to perform this action` при создании зоны | у аккаунта Fornex выключено право создавать зоны через API | создать зону в кабинете; панель показывает готовый текст тикета |
| `400 … already exists` при добавлении второй MX | Fornex сравнивает строку значения и не смотрит на приоритет | сделать значение отличным: точка на конце (`SMTP.GOOGLE.COM.`) или другой регистр |
| Запись «создана», но в зоне её нет | зона пересобирается минутами, публичный резолвер отвечает старым | спрашивать `ns1.fornex.com`, как это делает проверка |
| Две SPF в зоне → письма отбиваются с `permerror` | пресет применили к зоне с готовой SPF в обход слияния | удалить лишнюю по `id` и применить пресет снова: слияние сохранит незнакомые механизмы |
| DMARC остался `p=none`, хотя в пресете `quarantine` | пресет не понижает и не повышает политику сам | выставить `p=quarantine` осознанно |
| Проверка DKIM не проходит, запись на вид есть | значение вставлено фрагментами: потерян префикс `v=DKIM1; k=rsa;` или добавлен перенос | вставить строку целиком; нормализация добавит префикс и уберёт пробелы, но обрезанный ключ не спасёт |
| `405 method_not_allowed` | путь есть, метод другой | в ответе есть `Allow` и `details.allowed` |
| `409` при применении шага | шаг требует имя домена как подтверждение | передать `"confirm": "example.com"` (шаги DKIM и подтверждения) |

## Клиент API

| Симптом | Причина | Действие |
|---|---|---|
| Запросы висят без ответа | нет браузерного `User-Agent`, Fornex придерживает TLS-рукопожатие | `FORNEX_USER_AGENT` должен быть задан (по умолчанию задан) |
| `502 upstream_error` на всё | неверный хост в `FORNEX_BASE_URL` | нужен `https://ru.fornex.com/api`; `fornex.com/api/` отвечает 302 или 403 |
| Ответ через 30 секунд и ошибка | сеть или API медленные | поднять `FORNEX_TIMEOUT`; повторы действуют только на сеть и 5xx |
| Панель показывает версию старее кода | `APP_VERSION` не поднят | поднять: из него строится `?v=` |

## Отчёт проверки

| Симптом | Причина | Действие |
|---|---|---|
| «MX не найден», хотя запись в зоне есть | один запрос к резолверу не прошёл | повторить: отчёт спрашивает публичный резолвер и `ns1.fornex.com` раздельно |
| NS «не делегирован» у работающего домена | NS сменили минуту назад | подождать TTL реестра и проверить снова |
| PTR не совпадает | PTR принадлежит владельцу адреса, а не зоне | попросить владельца адреса поставить запись |

## Ничего не помогает

```bash
php tools/lint.php
php tests/plan_test.php
php backend/bin/fornex help          # проверяет настройки и базу
curl -s https://panel.example.com/fornex/api/health
tail -n 50 var/log/app.log
```

`var/log/app.log` пишет каждую неудачную операцию с причиной; `request_id` из строки журнала
совпадает с записью в журнале действий. В issue прикладывайте строку журнала, маршрут или
команду и состояние зоны — но никогда ключ API.
