# llama.cpp server: API-ключ, проверка доступа и границы защиты

Источник: https://modelium.tech/zhurnal/llama-cpp-api-key/

У llama-server можно включить проверку ключа через --api-key-file и отправлять его в заголовке Authorization: Bearer. На защищённом API-запросе без ключа ожидается HTTP 401, с верным ключом — доступ; однако /health и файлы веб-интерфейса остаются публичными. Ключ не шифрует трафик и не заменяет закрытый сетевой периметр.

**Версия документации и кода:** коммит llama.cpp `a7fb71f` от 4 октября 2026 года, проверено 5 октября 2026 года. Это инструкция для *вашего* тестового стенда, а не отчёт об испытании Modelium. Не открывайте пробный сервер с данными на публичном IP. [s1](#src-s1) [s2](#src-s2) [s5](#src-s5)

## Зачем llama-server нужен ключ, если модель работает локально?

Один процесс `llama-server` предоставляет HTTP API для локальной GGUF-модели. Без настроенных ключей middleware пропускает запросы; по умолчанию сервер привязывается к `127.0.0.1:8080`, но при смене адреса привязки круг возможных клиентов меняется. Ключ помогает отсечь запросы к защищённым endpoint, **но не делает открытый сервер безопасным для интернета**. [s1](#src-s1) [s2](#src-s2) [s3](#src-s3) [s5](#src-s5)

Сценарий этой страницы — разработчик с собственной совместимой GGUF-моделью и *тестовым* локальным сервером. Сначала проверьте, что модель загружается по [обычному гайду llama.cpp](/zhurnal/llama-cpp/). Файл весов, его размер и демонстрационный путь `-m` не являются гарантией доступной RAM/VRAM или качества на вашем устройстве; для выбранной модели отдельно проверьте формат, лицензию, ресурсы и ответ. Проверяемая цепочка: **задача клиента → конкретная GGUF → собственное железо → llama-server на localhost → ответ/отказ по ключу → анализ сетевой границы**. [s1](#src-s1) [s5](#src-s5)

Это не руководство по безопасной публикации сервера в недоверенной сети: `SECURITY.md` llama.cpp прямо предостерегает от использования функциональности `llama-server` в таком окружении. Предположение «добавлю ключ, потом открою порт наружу» нарушает границу задачи. Для удалённого доступа нужен отдельно спроектированный защищённый периметр; даже при наличии обратного прокси и TLS это не отменяет предупреждение проекта и вашей оценки угроз. [s1](#src-s1) [s5](#src-s5)

## Как включить проверку ключа без секретов в командной строке?

На **собственной** машине подготовьте текстовый файл `/path/to/keys.txt`: одна длинная случайная строка на строку, без пробелов и без комментария с секретом. Защитите доступ к файлу правами ОС и не помещайте его в репозиторий или резервные копии без шифрования. Параметр `--api-key-file` читает непустые строки, кроме начинающихся с `#`; пустой файл — плохая конфигурация, потому что при отсутствии загруженных ключей проверка вообще пропускается. Для устойчивой проверки убедитесь, что файл реально содержит ключ и что сервер его прочитал. [s2](#src-s2) [s3](#src-s3)

После установки llama.cpp и выбора вашего совместимого GGUF, в локальном тесте запустите `llama-server -m /path/to/model.gguf --host 127.0.0.1 --port 8080 --api-key-file /path/to/keys.txt --no-ui`. Здесь оба пути — **заменяемые примеры**, не существующие файлы Modelium. README подтверждает флаги `--host`, `--port`, `--api-key-file` и `--no-ui`; наличие именно вашего бинарника, поддерживаемого backend и доступной памяти проверяется до теста. Не включайте экспериментальные `--tools`, `--agent` и `--ui-mcp-proxy` для простого API: они расширяют возможности и угрозы. [s1](#src-s1) [s3](#src-s3)

`--api-key KEY` и переменная `LLAMA_API_KEY` также поддерживаются, но для долгоживущего процесса файл помогает не прописывать секрет прямо в аргументы запуска. Это **снижение** риска случайного попадания в историю команд, а не гарантия тайны: доступ к файлу и конфигурации сервиса всё равно надо контролировать. Если ключей несколько, README допускает список в параметре или строки в файле; не объявляйте это системой прав пользователей — middleware лишь сверяет полученное значение со списком. [s1](#src-s1) [s2](#src-s2) [s3](#src-s3)

## Как проверить 401 и успешный запрос?

Дождитесь загрузки модели: `GET /health` может отвечать 503 в фазе загрузки и 200 с `status: ok` при готовности. Этот endpoint **публичен даже при заданном ключе** — используйте его только для readiness, не для проверки закрытости API. Пробный запрос `curl -i http://127.0.0.1:8080/health` не должен содержать секрет. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4)

На той же машине выполните `curl -i http://127.0.0.1:8080/v1/models` **без** заголовка авторизации: после готовности сервера ожидается HTTP **401** и `authentication_error`. Затем отправьте запрос на тот же адрес с `Authorization: Bearer <ВАШ_ТЕСТОВЫЙ_КЛЮЧ>`; защищённый `/v1/models` должен вернуть **200** со списком модели. Не записывайте настоящий ключ в историю shell, лог команд, скриншот или статью; подстановка через защищённое хранилище нужна на вашей машине. Это ожидаемые результаты согласно коду и тестам проекта, не протокол прогона Modelium. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4)

Для полноты проверьте неверный ключ: он тоже должен дать 401, а правильный ключ в `X-Api-Key` также поддерживается middleware. `OPTIONS` preflight может проходить без ключа, чтобы браузер сделал CORS-проверку; это не означает, что последующий `GET`/`POST` освобождён от авторизации. Сохраните статусы и заголовки ответов без значения ключа, не логируя содержимое чувствительных запросов. [s2](#src-s2) [s4](#src-s4)

Если нужен именно **результат модели**, после успешного `/v1/models` отправьте один `POST /v1/chat/completions` со своим `model` и коротким тестовым сообщением, а затем проверьте непустой ответ и соответствие ID выбранной модели. README описывает этот endpoint, но предупреждает, что совместимость OpenAI API не абсолютна и зависит от chat template конкретной модели. Ответ на `/v1/models` проверяет доступ и метаданные, а не качество инференса. Modelium этот вызов и запуск модели не выполнял. [s1](#src-s1) [s4](#src-s4)

## Что остаётся доступным и почему CORS не заменяет ключ?

Список исключений в `server-http.cpp` включает `/health`, `/v1/health` и встроенные ресурсы UI: они доступны без ключа. Поэтому по одному ключу нельзя обещать, что все URL сервера скрыты. `--no-ui` отключает отображение интерфейса, но не меняет факт публичного readiness; после запуска проверьте фактические ответы своей сборки. В официальных unit-тестах проверены и публичный `/health`, и UI-assets, и 401 на защищённом пути. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4)

CORS определяет, какие браузерные origins могут читать ответы, а не кто может отправить HTTP-запрос из `curl` или другого сервиса. README фиксирует широкий CORS по умолчанию и рекомендует настройку для локальной сети; код отражает `Origin` при включённых credentials и разрешении `*`. Даже узкий `--cors-origins localhost` **не** является firewall, а запрет CORS не заменяет проверку Bearer. Для простого локального API не открывайте `0.0.0.0`; если приложение требует отдельного сетевого сервиса, рассмотрите другой изолированный контур и отдельную модель угроз. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4) [s5](#src-s5) [s6](#src-s6)

API-ключ передаётся заголовком, а не шифрует HTTP. `--ssl-key-file` и `--ssl-cert-file` существуют в документации, но сборка с SSL требует отдельного `LLAMA_OPENSSL=ON`; обратный прокси и TLS следует проектировать отдельно, не выдавая их наличие за результат установки одного флага. Рекомендации разработчиков для публичного сервера с proxy и ключом в README сосуществуют с более жёстким предупреждением `SECURITY.md` о недоверенных сетях; для этой страницы выбираем **более осторожный локальный сценарий**, а не обещаем безопасный публичный endpoint. [s1](#src-s1) [s3](#src-s3) [s5](#src-s5)

## Что делать при 200 без ключа, 401 с ключом или утечке?

- `**/v1/models**`** отвечает 200 без ключа:** проверьте, не подменился ли адрес/порт, не пуст ли файл ключей и прочитан ли он. Код при пустом `api_keys` отключает проверку; открытый `/health` для этого теста не годится. Остановите процесс и устраните конфигурационную ошибку до подключения клиентов. [s1](#src-s1) [s2](#src-s2) [s3](#src-s3)

- **401 с корректным на вид ключом:** сравните значение без вывода секрета в общие логи, проверьте заголовок `Authorization: Bearer`, строку файла ключей и то, что после изменения файла сервер перезапущен. Код загружает файл при разборе аргументов и сравнивает полученное значение, так что само изменение файла не подтверждает ротацию процесса. [s2](#src-s2) [s3](#src-s3)

- **Готовность 200, но модель не отвечает:** readiness и успешный инференс — разные проверки; сверьте модель, шаблон диалога, статус API и ресурсы машины. Код может отвечать 503 до готовности, а документация отдельно описывает `/v1/chat/completions`. [s1](#src-s1) [s2](#src-s2)

- **Ключ оказался в журнале или на чужой машине:** прекратите доступ в пределах вашего контура, замените значение, перезапустите сервер, проверьте логи и сетевые правила. Несанкционированные запросы нельзя исключить задним числом только по успешной смене ключа. [s2](#src-s2) [s3](#src-s3) [s5](#src-s5)

После проверки остановите тестовый процесс, удалите ненужный тестовый ключ из рабочего окружения и сохраните только обезличенные статусы HTTP. **Основной следующий шаг** — [подготовить собственный локальный llama-server](/zhurnal/llama-cpp/) с совместимой моделью, повторить 401/200-тест на `127.0.0.1` и отдельно проверить сетевую доступность на своём стенде. Встроенного подтверждения безопасности сети одним ключом нет; необходимость изолированного контура разберите в [гайде проверки офлайн-режима](/zhurnal/proverka-offline-kontura-ii/). [s1](#src-s1) [s2](#src-s2) [s5](#src-s5)

## Источники

1. [llama.cpp: llama-server README — flags, CORS, API and health](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/tools/server/README.md) — получено 2026-10-05
2. [llama.cpp: server-http.cpp — auth middleware and public endpoint list](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/tools/server/server-http.cpp) — получено 2026-10-05
3. [llama.cpp: common/arg.cpp — server flags and key-file parser](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/common/arg.cpp) — получено 2026-10-05
4. [llama.cpp: test_security.py — public routes, 401, valid key and CORS](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/tools/server/tests/unit/test_security.py) — получено 2026-10-05
5. [llama.cpp: SECURITY.md — untrusted networks and model isolation](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/SECURITY.md) — получено 2026-10-05
6. [llama.cpp: server.cpp — routes and security warning](https://github.com/ggml-org/llama.cpp/blob/a7fb71fab83b474a0892b9a05aaa3a8ddca2729b/tools/server/server.cpp) — получено 2026-10-05
