> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/ru/reference/faq.md).

# Часто задаваемые вопросы

Каждый ответ ниже описывает фактическое поведение в коде, а не догадки. Если что-то здесь не совпадает с вашим экземпляром, вероятно, у вас другая версия.

## Развертывание

<details>

<summary>Каждый вызов API возвращает 403 "Доступ запрещён" или "Что вы делаете?"</summary>

Глобальный referer-гейт отклонил запрос. Два разных сообщения:

* `{"error":"Что вы делаете?"}` — запрос содержал **никакого** `Referer` заголовок.
* `{"error":"Доступ запрещён"}` — был `Referer` отправлен, но его hostname не разрешён.

Разрешённые hostname: `localhost` плюс всё из `ALLOWED_DOMAINS`. Установите его в hostname(ы), с которых ваши пользователи фактически загружают сайт:

{% code title=".env" %}

```bash
ALLOWED_DOMAINS="myip.example.com,www.myip.example.com"
```

{% endcode %}

Затем перезапустите бэкенд. Людей сбивают с толку три вещи:

1. **Совпадение точное.** `example.com` не охватывает `sub.example.com`. Перечислите каждый hostname.
2. **Доступ по «сырому» IP считается hostname.** Переход к `http://192.168.1.10:18966` отправляет этот IP как hostname referer — добавьте его или используйте hostname.
3. **Порт и схема не имеют значения**, сравнивается только hostname.

См. [Параметры безопасности](/developer/ru/configuration/security-options.md).

</details>

<details>

<summary>Переменная VITE_* не влияет после перезапуска контейнера</summary>

`VITE_*` переменные читаются Vite во время `pnpm run build` и **встроены в JavaScript-бандл**. Они не читаются во время выполнения. Перезапуск сервера не может изменить значение, которое уже скомпилировано в `dist/`.

Официальный `jason5ng32/myip:latest` образ был собран без ваших значений — `.env` находится в `.dockerignore`, поэтому `.env` тоже не было во время той сборки. Передача `-e VITE_CURL_IPV4_DOMAIN=...` в предварительно собранный образ ничего не делает с фронтендом. Чтобы использовать переменные времени сборки в Docker, нужно собрать свой собственный образ. В развертывании Node повторно выполните `pnpm run build`.

Две `VITE_*` переменные также читаются во время выполнения и *действительно* реагируют на `-e`: `VITE_SENTRY_DSN_FRONTEND` (который монтирует `/api/monitoring`) и `VITE_SITE_URL` (который собирает upstream `User-Agent`). Полная разбивка в [Переменные окружения](/developer/ru/reference/environment-variables.md).

</details>

<details>

<summary>Порт 18966 уже используется, или я хочу другие порты</summary>

MyIP запускает два слушателя: статический / SPA-сервер на `FRONTEND_PORT` (по умолчанию `18966`) и API-сервер на `BACKEND_PORT` (по умолчанию `11966`). Фронтенд-сервер проксирует `/api` на бэкенд, поэтому снаружи должен быть доступен только порт фронтенда.

**Развертывание Node** — задайте `BACKEND_PORT` и `FRONTEND_PORT` в `.env` и перезапустите. Оба читаются `backend-server.js`, `frontend-server.js` и `vite.config.js`, поэтому изменение одного без другого ломает прокси.

**Docker** — не меняйте внутренний порт контейнера; вместо этого переназначьте его на стороне хоста. Образ `EXPOSE`s `18966`:

{% code title="оболочка" %}

```bash
docker run -d -p 8080:18966 --name myip --restart always jason5ng32/myip:latest
```

{% endcode %}

</details>

<details>

<summary>Карточка curl API никогда не появляется</summary>

Промежуточный слой `curlDomainsHadSet` геттер в `frontend/store.js` объединяет три домена по AND, поэтому карточка отображается только когда **все три** не пустые. Если задать один или два, ничего не покажется. Задайте `VITE_CURL_IPV4_DOMAIN`, `VITE_CURL_IPV6_DOMAIN` и `VITE_CURL_IPV64_DOMAIN` вместе — и помните, что они `VITE_*`, поэтому нужен не просто перезапуск, а пересборка.

Эти переменные лишь задают hostname для отображения. MyIP не обслуживает эти конечные точки; вы указываете DNS-записи на свой собственный текстовый сервис-эхо IP.

</details>

<details>

<summary>Пользователи получают 429 "Слишком много запросов"</summary>

`SECURITY_RATE_LIMIT` установлено, и клиент его превысил. Окно составляет **20 минут** на каждый IP клиента, и при превышении возвращается `429 {"message":"Слишком много запросов"}`.

{% hint style="info" %}
В стартовом баннере написано `🛡️ Ограничитель частоты включён — N запросов за 60 минут`, но настроенное окно в `backend-server.js` является `20 * 60 * 1000` мс. Сообщение в логах неверно; реальное окно — 20 минут.
{% endhint %}

Увеличьте число или установите его в `0` чтобы полностью отключить ограничитель. `SECURITY_DELAY_AFTER` это отдельный, более мягкий механизм — он никогда не отклоняет, а лишь добавляет `запросов × 400 мс` задержки после N запросов за **60-минутное** окно.

Одна загрузка страницы MyIP отправляет много `/api` запросов, поэтому низкий лимит ударит по обычным пользователям. В логах есть `IP ограничен по частоте` предупреждение с проблемным IP — один раз при переходе в состояние ограничения, а не на каждый заблокированный запрос. Установите `SECURITY_BLACKLIST_LOG_FILE_PATH` если вам также нужен постоянный журнал на диске.

</details>

## MaxMind

<details>

<summary>В логах сказано: "MaxMind API будет возвращать 503, пока базы данных не будут успешно загружены"</summary>

Бэкенд не смог открыть `common/maxmind-db/GeoLite2-City.mmdb` и `GeoLite2-ASN.mmdb`. Он всё равно запускается, но `GET /api/maxmind` отвечает 503, а значки стран во всём интерфейсе остаются пустыми.

Обычно сначала видите такое предупреждение:

{% code title="журнал" %}

```
⚠️  Базы данных MaxMind отсутствуют, и MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY не настроены.
  Укажите учётные данные в .env и перезапустите, или поместите GeoLite2-City.mmdb + GeoLite2-ASN.mmdb
  в common/maxmind-db/. Сервер всё равно запускается; MaxMind API будет возвращать 503, пока
  базы данных не станут доступны.
```

{% endcode %}

Исправление ровно то, что сказано в сообщении — задайте учётные данные или заранее положите файлы:

{% code title=".env" %}

```bash
MAXMIND_ACCOUNT_ID="your-account-id"
MAXMIND_LICENSE_KEY="your-license-key"
MAXMIND_AUTO_UPDATE="true"
```

{% endcode %}

Успешный результат выглядит так `📦 Базы данных MaxMind загружены (запуск)`. Полное руководство в [Настройка MaxMind](/developer/ru/getting-started/maxmind-setup.md).

</details>

<details>

<summary>У свежего Docker-контейнера пустой каталог maxmind-db</summary>

Это сделано намеренно. Базы GeoLite2 нельзя распространять из-за лицензии MaxMind, и `.dockerignore` исключает `common/maxmind-db/*.mmdb` поэтому локальная сборка никогда не встраивает файлы, которых не было бы в сборке CI.

Поэтому при развёртывании в Docker нужно использовать путь с учётными данными — передайте `MAXMIND_ACCOUNT_ID`, `MAXMIND_LICENSE_KEY` и `MAXMIND_AUTO_UPDATE="true"` с `-e`. Без них контейнер запускается, отдаёт интерфейс и возвращает 503 на `/api/maxmind` при каждой загрузке.

Первая загрузка происходит во время запуска и ограничена 5 минутами. Если происходит таймаут, сервер всё равно продолжает слушать — проверьте логи и перезапустите. См. [Развертывание с Docker](/developer/ru/getting-started/deploy-with-docker.md).

</details>

<details>

<summary>MAXMIND_AUTO_UPDATE имеет значение "false", но базы данных всё равно загрузились</summary>

Всё работает как задумано. `MAXMIND_AUTO_UPDATE` ограничивает **только периодический планировщик**. Путь "загрузить при отсутствии" во время запуска его не учитывает: если есть действительные учётные данные и `.mmdb` файлов нет, выполняется один цикл загрузки. Логика в том, что учётные данные в `.env` уже выражают намерение "Я хочу, чтобы MaxMind работал".

С `MAXMIND_AUTO_UPDATE="true"` вы также получаете первую проверку через 60 с после запуска и обновление каждые 24 ч. `CAIDA_AUTO_UPDATE` ведёт себя так же для наборов данных CAIDA.

</details>

## Сеть и API

<details>

<summary>curl к /api/... возвращает 403, но сайт работает в браузере</summary>

curl не отправляет `Referer` заголовок, поэтому глобальный gate отвечает `403 {"error":"Что вы делаете?"}`. Это не баг — в этом и состоит смысл gate.

Чтобы вручную проверить конечную точку, укажите разрешённый referer:

{% code title="оболочка" %}

```bash
curl -H "Referer: http://localhost/" http://localhost:11966/api/configs
```

{% endcode %}

Используйте hostname, указанный в `ALLOWED_DOMAINS` (или `localhost`, который всегда разрешён) и обращайтесь напрямую к порту бэкенда.

</details>

<details>

<summary>Некоторые источники IP-данных отсутствуют в интерфейсе</summary>

Фронтенд скрывает источники, для которых у бэкенда нет API-ключа. `GET /api/configs` возвращает по одному булевому значению на каждую функцию — никогда не сами значения ключей. Получите его (с действительным `Referer`) , чтобы точно увидеть, что ваш экземпляр считает настроенным: `map` требуется `GOOGLE_MAP_API_KEY`, `ipapiis` требуется `IPAPIIS_API_KEY`, `cloudFlare` требуется `CLOUDFLARE_API_KEY`, и так далее. Полный список полей находится в [Конечные точки API](/developer/ru/reference/api-endpoints.md); ключи находятся в [Необязательные API-ключи](/developer/ru/configuration/optional-api-keys.md).

Учтите, что этот маршрут кэшируется на edge на 1 час, поэтому новому ключу может потребоваться столько времени, чтобы появиться за CDN.

</details>

<details>

<summary>/api/ipapiis или /api/ip2location возвращает 500</summary>

Эти два обработчика строят upstream-URL, вызывая `.split(',')` к ключу без проверки на null, поэтому не заданный ключ вызывает исключение и даёт 500 вместо аккуратной ошибки. Установите `IPAPIIS_API_KEY` или `IP2LOCATION_API_KEY`.

При обычном использовании вы этого никогда не видите: `/api/configs` сообщает, что источник недоступен, и фронтенд не вызывает его.

</details>

<details>

<summary>Обмен отчётами возвращает 503 "Обмен отчётами не настроен"</summary>

`POST /api/report` и `GET /api/report/:id` требуют **все три** переменные Cloudflare Workers KV: `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID` и `CLOUDFLARE_KV_NAMESPACE_ID`. Отсутствие любого из них означает 503 на обоих маршрутах и `reportSharing: false` в `/api/configs`, что полностью скрывает интерфейс обмена.

Две распространённые ошибки: токену нужны **Workers KV Storage: Редактирование** разрешение (обычного токена Radar недостаточно), а `CLOUDFLARE_KV_NAMESPACE_ID` это у пространства имён **hex ID** из панели управления, а не его отображаемое имя.

</details>

<details>

<summary>События Sentry во фронтенде получают 404 при обращении к /api/monitoring</summary>

`backend-server.js` монтирует маршрут туннеля только когда `VITE_SENTRY_DSN_FRONTEND` задано **в процессе сервера**. Если вы встроили DSN в собственный образ во время сборки, но не передали его также во время выполнения, бандл отправляет envelope на маршрут, который никогда не был смонтирован.

Передайте одно и то же значение в обоих местах. См. [Мониторинг ошибок (Sentry)](/developer/ru/configuration/error-monitoring.md).

</details>

## Разработка

<details>

<summary>npm install ломает проект</summary>

MyIP использует **только pnpm**. Версия закреплена через `packageManager` в `package.json`, `pnpm-lock.yaml` закоммичен, а `pnpm-workspace.yaml` содержит разрешения на скрипты установки, которые нужны нативным зависимостям. npm или yarn создали бы конкурирующий lockfile и не унаследовали бы эти разрешения.

{% code title="оболочка" %}

```bash
npm install -g pnpm
pnpm install && pnpm run build
```

{% endcode %}

Dockerfile делает то же самое через `corepack enable`, который обеспечивает именно зафиксированную версию pnpm.

</details>

<details>

<summary>Сервер разработки не может достучаться до бэкенда</summary>

`pnpm dev` запускает Vite и бэкенд одновременно. Vite отдаёт фронтенд на `FRONTEND_PORT` и проксирует `/api` к `http://localhost:${BACKEND_PORT}`. Если вы изменили один порт, но не другой — или задали их только в shell, а не в `.env` — прокси указывает в никуда.

Оба `vite.config.js` и `backend-server.js` читают одни и те же две переменные из `.env`, поэтому держите их там. См. [Среда разработки](/developer/ru/development/dev-environment.md).

</details>

<details>

<summary>Ничего не логируется, кроме ошибок</summary>

`LOG_LEVEL` по умолчанию `info`, поэтому `debug` строки подавляются. HTTP-логирование по каждому запросу полностью выключено, если только вы не включите его:

{% code title=".env" %}

```bash
LOG_LEVEL="debug"
LOG_HTTP="true"
```

{% endcode %}

`LOG_HTTP=true` логирует по одной строке на `/api/*` запрос с методом, URL и статусом. Он монтируется до ограничителя частоты, поэтому 429 тоже видны. Установите `LOG_FORMAT="json"` если вывод читает shipper логов. См. [Логирование](/developer/ru/configuration/logging.md).

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/ru/reference/faq.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
