diff --git a/docs/auth/browser-keys.mdx b/docs/auth/browser-keys.mdx
new file mode 100644
index 0000000..3373d29
--- /dev/null
+++ b/docs/auth/browser-keys.mdx
@@ -0,0 +1,197 @@
+---
+title: Browser Keys
+description: Use a FastNear API key directly from a browser app by restricting it to your website's domains, with a per-key rate cap and one-click rotation.
+slug: /auth/browser-keys
+sidebar_position: 1
+displayed_sidebar: rpcSidebar
+page_actions:
+ - markdown
+keywords:
+ - browser api key
+ - frontend api key
+ - restrict api key to domain
+ - restrict api key to website
+ - origin
+ - referer
+ - CORS
+ - website restriction
+ - ui api key
+ - public api key
+---
+
+import Link from '@docusaurus/Link';
+import IconExternalLink from '@theme/Icon/ExternalLink';
+
+# Browser Keys
+
+A FastNear API key normally lives on a backend. But if your app talks to FastNear directly from the
+browser, you can publish a key in your frontend and **restrict it to your website's domains** so a
+copied key is hard to reuse elsewhere. This is the *browser key* (or *UI key*) posture.
+
+Two postures, one credential model:
+
+| Posture | Where the key lives | How it is protected |
+| --- | --- | --- |
+| **Server key** (default) | backend, worker, proxy, secret manager | kept secret; never shipped to clients. See [Auth & Access](/auth). |
+| **Browser key** | shipped in your frontend bundle | restricted to your site's `Origin`/`Referer`, plus a per-key rate cap. This page. |
+
+A browser key is **not a secret** — anyone can read it from your page source. The restriction makes it
+inconvenient to reuse and the rate cap limits the damage if it is. For anything that must stay secret,
+use a server key.
+
+:::info Availability
+Website restrictions are a **paid-plan** feature. On the free tier the control is disabled — upgrade
+your project in FastNear Dashboard
+to enable browser keys.
+:::
+
+## If you only need the rule
+
+- Create a key in the dashboard, then turn on **Restrict to websites** and list your domains.
+- A restricted key only works from a browser on a matching `Origin` (or `Referer`); everything else gets
+ `403 origin_not_allowed` — including your own backend.
+- Calling from the browser works with either `Authorization: Bearer ...` or `?apiKey=...`; FastNear's RPC
+ answers the CORS preflight, so both forms work.
+- Set a **Rate cap** on the key so a lifted key cannot burn your whole plan's quota.
+- A restriction is a **deterrent, not a secret**. Keep a separate, unrestricted server key for backend work.
+
+## Restrict a key to your website
+
+1. Open your project in FastNear Dashboard
+ and create (or pick) an API key.
+2. Turn on **Restrict to websites**.
+3. List the domains your app is served from, comma-separated:
+
+ ```text
+ example.com, *.example.com
+ ```
+
+ - Matching is **exact** on the hostname. `example.com` does **not** cover `www.example.com` or
+ `app.example.com` — add each host, or use a wildcard.
+ - `*.example.com` matches any direct subdomain (`app.example.com`, `www.example.com`).
+ - You can list up to **20 domains** per key.
+ - Do not include the scheme, port, or path — just the hostname (`app.example.com`, not
+ `https://app.example.com/`).
+
+Turning the restriction **on** hard-blocks every caller without a matching `Origin`/`Referer`, *including
+your own server-side traffic with that key*. Use a separate unrestricted key for backend calls.
+
+## How the restriction is enforced
+
+The check runs at the edge, before the request reaches the RPC, against the browser-set `Origin` header
+(with `Referer` as a fallback):
+
+| Request | Result |
+| --- | --- |
+| `Origin` matches an allowed domain | ✅ passes |
+| No `Origin`, but `Referer` matches | ✅ passes |
+| `Origin` present but not allowed | ⛔ `403 origin_not_allowed` |
+| Neither `Origin` nor `Referer` (e.g. curl, a backend) | ⛔ `403 origin_not_allowed` |
+| A subdomain of an allowed domain, with no wildcard | ⛔ `403 origin_not_allowed` |
+
+`Origin` takes precedence over `Referer` — a request with a disallowed `Origin` is rejected even if the
+`Referer` would have matched. The `403` body is `{"error":"origin_not_allowed"}`.
+
+A key with **no** website restriction is treated as a server key: the origin check is skipped and it works
+from anywhere (so keep it secret).
+
+## Call FastNear from the browser
+
+FastNear's RPC endpoint returns permissive CORS headers and answers the preflight `OPTIONS` request, so a
+browser `fetch()` works directly — no proxy required.
+
+```js
+// Served from https://app.example.com (an allowed domain on this key)
+const FASTNEAR_API_KEY = 'YOUR_BROWSER_KEY'; // restricted to app.example.com
+
+const res = await fetch('https://rpc.mainnet.fastnear.com', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${FASTNEAR_API_KEY}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ jsonrpc: '2.0',
+ id: 1,
+ method: 'block',
+ params: { finality: 'final' },
+ }),
+});
+const data = await res.json();
+```
+
+The browser sends your site's `Origin` automatically, so the edge can enforce the restriction. The
+`?apiKey=` query form works too and is handy when setting headers is awkward:
+
+```js
+const res = await fetch(`https://rpc.mainnet.fastnear.com?apiKey=${FASTNEAR_API_KEY}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'block', params: { finality: 'final' } }),
+});
+```
+
+:::note
+If a browser call exceeds your rate limit, the `429` response may not yet include CORS headers, so the
+browser can surface it as a generic network error rather than the JSON limit message. Treat a sudden
+`fetch` failure under load as a possible rate-limit hit.
+:::
+
+## Limit the blast radius
+
+A published key will eventually be copied. Two controls keep that cheap:
+
+- **Rate cap** — set a per-key cap (requests per minute) below your plan default. A lifted key can then
+ only consume up to that cap, not your whole quota. Leave it blank to use the plan default.
+- **Rotate** — if a key is abused, use **Rotate** to issue a new value while keeping the same
+ restrictions and cap. Update your site with the new value; the old value stops working at the edge
+ shortly after, once the change syncs.
+
+## This is a deterrent, not a secret
+
+:::caution
+`Origin` and `Referer` are set by the browser, but a non-browser client (curl, a script) can forge them.
+So a website restriction stops **casual reuse of a key lifted from your page**, but it is **not** a hard
+security boundary.
+:::
+
+What this means in practice:
+
+- The risk of a leaked browser key is **quota and cost abuse** — someone running up usage against your
+ plan. FastNear API keys are rate-limit/billing credentials, not NEAR account keys, so a leaked browser
+ key cannot move funds or sign transactions on your accounts.
+- Your real backstops are the **rate cap** (bounds the damage) and **rotation** (ends it).
+- Never put a credential that must stay secret in the browser. Use a [server key](/auth) behind your
+ backend for anything sensitive or unrestricted.
+
+## Common failure modes
+
+### `403 origin_not_allowed` from curl or my backend
+
+Expected — a restricted key only works from a browser on an allowed domain. Backends and curl send no
+`Origin`/`Referer`. Use a separate unrestricted server key for backend traffic.
+
+### It works on `example.com` but not `www.example.com`
+
+Hostname matching is exact. Add `www.example.com` to the key, or use `*.example.com` to cover subdomains.
+
+### My browser call fails with no useful error
+
+If you are over your rate limit, the `429` may arrive without CORS headers and show up as a generic
+network error. Check your usage, and consider whether your key's rate cap is too low.
+
+### The docs UI worked, but my app does not
+
+The docs site can forward a saved key for convenience; that is not the production pattern. Ship your own
+restricted browser key, served from a domain you listed on the key.
+
+### I need both browser and backend access
+
+Use two keys: a restricted browser key for the frontend, and an unrestricted server key for the backend.
+Do not try to share one key across both.
+
+## Related guides
+
+- [Auth & Access](/auth) — the server-key default and transport choices
+- [Auth for Agents](/agents/auth) — runtime posture for agents and automations
+- [RPC Reference](/rpc)
diff --git a/docs/auth/index.mdx b/docs/auth/index.mdx
index fe5a6f7..3772c24 100644
--- a/docs/auth/index.mdx
+++ b/docs/auth/index.mdx
@@ -38,6 +38,7 @@ Live example pages also support `Copy example URL` for sharing prefilled request
- Prefer `Authorization: Bearer ${FASTNEAR_API_KEY}` for production backends.
- Use `?apiKey=${FASTNEAR_API_KEY}` when headers are awkward or you are doing quick curl/debug work.
- Agents and automations should keep the key in env vars or a secret manager, not in browser storage.
+- Building a browser or frontend app? Ship a [browser key](/auth/browser-keys) restricted to your domains, not a plain server key.
## Choose the auth form
@@ -107,3 +108,7 @@ Do not rely on browser storage behavior from the docs UI. Production backends sh
### I am building an agent or automation
Use [Auth for Agents](/agents/auth) for the runtime posture. Browser `localStorage` is a docs convenience, not an agent secret store.
+
+### I am building a browser or frontend app
+
+Do not ship a server key in your frontend. Use a [browser key](/auth/browser-keys) restricted to your site's domains, with a per-key rate cap, so a copied key is hard to reuse and cannot drain your quota.
diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/auth/browser-keys.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/auth/browser-keys.mdx
new file mode 100644
index 0000000..ff26a2b
--- /dev/null
+++ b/i18n/ru/docusaurus-plugin-content-docs/current/auth/browser-keys.mdx
@@ -0,0 +1,170 @@
+---
+title: "Браузерные ключи"
+description: "Используйте API-ключ FastNear прямо из браузерного приложения, ограничив его доменами своего сайта, с лимитом частоты на ключ и ротацией в один клик."
+slug: /auth/browser-keys
+sidebar_position: 1
+displayed_sidebar: rpcSidebar
+page_actions:
+ - markdown
+keywords:
+ - browser api key
+ - frontend api key
+ - restrict api key to domain
+ - restrict api key to website
+ - origin
+ - referer
+ - CORS
+ - website restriction
+ - ui api key
+ - public api key
+ - браузерный ключ
+ - ограничение по сайтам
+ - привязка к домену
+ - подписка
+---
+
+import Link from '@docusaurus/Link';
+import IconExternalLink from '@theme/Icon/ExternalLink';
+
+# Браузерные ключи
+
+Обычно API-ключ FastNear живёт на бэкенде. Но если приложение обращается к FastNear напрямую из браузера, можно опубликовать ключ во фронтенде и **ограничить его доменами своего сайта** — тогда скопированный ключ трудно использовать где-то ещё. Это режим *браузерного ключа* (или *UI-ключа*).
+
+Два режима, одна модель учётных данных:
+
+| Режим | Где живёт ключ | Как защищён |
+| --- | --- | --- |
+| **Серверный ключ** (по умолчанию) | бэкенд, воркер, прокси, менеджер секретов | хранится в секрете; никогда не отправляется клиентам. См. [Аутентификацию и доступ](/auth). |
+| **Браузерный ключ** | поставляется в сборке фронтенда | ограничен по `Origin`/`Referer` сайта плюс лимит частоты на ключ. Эта страница. |
+
+Браузерный ключ — это **не секрет**: его может прочитать любой из исходного кода страницы. Ограничение делает повторное использование неудобным, а лимит частоты снижает ущерб, если ключ всё же скопировали. Для всего, что должно оставаться секретом, используйте серверный ключ.
+
+:::info Доступность
+Ограничение по сайтам — функция платных тарифов. На бесплатном тарифе этот элемент управления отключён — повысьте тариф проекта в FastNear Dashboard, чтобы включить браузерные ключи.
+:::
+
+## Если нужно только правило
+
+- Создайте ключ в панели управления, включите **Restrict to websites** и перечислите свои домены.
+- Ключ с ограничением работает только из браузера на подходящем `Origin` (или `Referer`); всё остальное получает `403 origin_not_allowed` — включая собственный бэкенд.
+- Вызов из браузера работает и через `Authorization: Bearer ...`, и через `?apiKey=...`; RPC FastNear отвечает на предварительный запрос `CORS`, поэтому работают оба варианта.
+- Задайте лимит частоты (**Rate cap**) для ключа, чтобы украденный ключ не сжёг всю квоту тарифа.
+- Ограничение — это сдерживающая мера, а **не** секрет. Держите отдельный серверный ключ без ограничений для работы с бэкендом.
+
+## Привязка ключа к сайту
+
+1. Откройте проект в FastNear Dashboard и создайте (или выберите) API-ключ.
+2. Включите **Restrict to websites**.
+3. Перечислите через запятую домены, с которых обслуживается приложение:
+
+ ```text
+ example.com, *.example.com
+ ```
+
+ - Совпадение по имени хоста — точное. `example.com` не покрывает `www.example.com` или `app.example.com` — добавьте каждый хост или используйте подстановочный шаблон.
+ - `*.example.com` совпадает с любым прямым поддоменом (`app.example.com`, `www.example.com`).
+ - На один ключ можно указать до **20 доменов**.
+ - Не указывайте схему, порт или путь — только имя хоста (`app.example.com`, а не `https://app.example.com/`).
+
+Включение ограничения жёстко блокирует любой вызов без подходящего `Origin`/`Referer`, *включая собственный серверный трафик с этим ключом*. Для вызовов с бэкенда используйте отдельный ключ без ограничений.
+
+## Как применяется ограничение
+
+Проверка выполняется на периметре, до того как запрос дойдёт до RPC, по выставленному браузером заголовку `Origin` (с запасным вариантом `Referer`):
+
+| Запрос | Результат |
+| --- | --- |
+| `Origin` совпадает с разрешённым доменом | ✅ проходит |
+| нет `Origin`, но совпадает `Referer` | ✅ проходит |
+| `Origin` есть, но не в списке | ⛔ `403 origin_not_allowed` |
+| нет ни `Origin`, ни `Referer` (например, curl или бэкенд) | ⛔ `403 origin_not_allowed` |
+| поддомен разрешённого домена без подстановочного шаблона | ⛔ `403 origin_not_allowed` |
+
+`Origin` имеет приоритет над `Referer` — запрос с недопустимым `Origin` отклоняется, даже если `Referer` подошёл бы. Тело ответа `403` — `{"error":"origin_not_allowed"}`.
+
+Ключ **без** ограничения по сайтам считается серверным: проверка источника пропускается, и он работает откуда угодно (поэтому держите его в секрете).
+
+## Вызов FastNear из браузера
+
+RPC-эндпоинт FastNear возвращает разрешающие заголовки `CORS` и отвечает на предварительный запрос `OPTIONS`, поэтому браузерный `fetch()` работает напрямую — прокси не нужен.
+
+```js
+// Запущено на https://app.example.com (разрешённый домен этого ключа)
+const FASTNEAR_API_KEY = 'YOUR_BROWSER_KEY'; // ограничен доменом app.example.com
+
+const res = await fetch('https://rpc.mainnet.fastnear.com', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${FASTNEAR_API_KEY}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ jsonrpc: '2.0',
+ id: 1,
+ method: 'block',
+ params: { finality: 'final' },
+ }),
+});
+const data = await res.json();
+```
+
+Браузер автоматически отправляет `Origin` сайта, поэтому периметр может применить ограничение. Форма с параметром `?apiKey=` тоже работает и удобна, когда выставлять заголовки неудобно:
+
+```js
+const res = await fetch(`https://rpc.mainnet.fastnear.com?apiKey=${FASTNEAR_API_KEY}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'block', params: { finality: 'final' } }),
+});
+```
+
+:::note
+Если браузерный вызов превышает лимит частоты, ответ `429` пока может не содержать заголовков `CORS`, поэтому браузер покажет его как обычную сетевую ошибку, а не как JSON-сообщение о лимите. Внезапный сбой `fetch` под нагрузкой стоит трактовать как возможное срабатывание лимита.
+:::
+
+## Ограничение масштаба ущерба
+
+Опубликованный ключ рано или поздно скопируют. Два механизма удерживают цену этого низкой:
+
+- **Лимит частоты (Rate cap)** — задайте лимит на ключ (запросов в минуту) ниже тарифного по умолчанию. Тогда украденный ключ сможет потратить не больше этого лимита, а не всю квоту. Оставьте поле пустым, чтобы использовать значение тарифа по умолчанию.
+- **Ротация (Rotate)** — при злоупотреблении ключом выпустите новое значение, сохранив те же ограничения и лимит. Обновите сайт новым значением; старое значение перестаёт работать на периметре вскоре после синхронизации изменения.
+
+## Это сдерживающая мера, а не секрет
+
+:::caution
+Заголовки `Origin` и `Referer` выставляет браузер, но небраузерный клиент (curl, скрипт) может их подделать. Поэтому ограничение по сайтам останавливает **случайное** повторное использование ключа, вытащенного со страницы, но это **не** строгая граница безопасности.
+:::
+
+Что это значит на практике:
+
+- Риск утёкшего браузерного ключа — злоупотребление **квотой и расходами**: кто-то накручивает потребление по вашему тарифу. API-ключи FastNear — это учётные данные для лимитов и биллинга, а не ключи аккаунта NEAR, поэтому утёкший браузерный ключ не может переводить средства или подписывать транзакции по вашим аккаунтам.
+- Настоящая подстраховка — это **лимит частоты** (ограничивает ущерб) и **ротация** (прекращает его).
+- Никогда не помещайте в браузер учётные данные, которые должны оставаться секретом. Для всего чувствительного или неограниченного используйте [серверный ключ](/auth) за своим бэкендом.
+
+## Частые проблемы
+
+### `403 origin_not_allowed` от curl или с бэкенда
+
+Так и должно быть — ключ с ограничением работает только из браузера на разрешённом домене. Бэкенды и curl не отправляют `Origin`/`Referer`. Для бэкенд-трафика используйте отдельный серверный ключ без ограничений.
+
+### Работает на `example.com`, но не на `www.example.com`
+
+Совпадение по имени хоста — точное. Добавьте `www.example.com` к ключу или используйте `*.example.com`, чтобы покрыть поддомены.
+
+### Браузерный вызов падает без внятной ошибки
+
+При превышении лимита частоты ответ `429` может прийти без заголовков `CORS` и выглядеть как обычная сетевая ошибка. Проверьте потребление и подумайте, не слишком ли низкий лимит частоты у ключа.
+
+### В UI документации сработало, а в приложении — нет
+
+Сайт документации может подставлять сохранённый ключ для удобства — это не продовый шаблон. Поставляйте собственный браузерный ключ с ограничением, обслуживаемый с домена, указанного в ключе.
+
+### Нужен доступ и из браузера, и с бэкенда
+
+Используйте два ключа: браузерный ключ с ограничением для фронтенда и серверный ключ без ограничений для бэкенда. Не пытайтесь использовать один ключ для обоих случаев.
+
+## Связанные руководства
+
+- [Аутентификация и доступ](/auth) — серверный ключ по умолчанию и выбор способа передачи
+- [Аутентификация для агентов](/agents/auth) — режим выполнения для агентов и автоматизаций
+- [Справочник RPC](/rpc)
diff --git a/sidebars.js b/sidebars.js
index 648be21..34f4e62 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -89,6 +89,7 @@ const rpcSidebar = withExamplesFooter(
...nearcoreReleaseSidebarItems,
'rpc/index',
'auth/index',
+ 'auth/browser-keys',
{
type: 'category',
label: 'Account',