Обход ограничений Elevenlabs Audio Agents для пользователей из РФ

Содержание скрыть




Полное руководство по настройке ElevenLabs ConvAI виджета через Node.js прокси на VPS

Полное руководство по настройке
ElevenLabs ConvAI виджета
через Node.js прокси на VPS

Версия 1.0 | Октябрь 2025

Оглавление

Часть 1: Введение и архитектура

1.1 Описание проблемы

ElevenLabs ConvAI виджет при прямом использовании сталкивается с двумя критическими проблемами:

  1. CORS (Cross-Origin Resource Sharing) ошибки – браузер блокирует запросы из-за отсутствия необходимых заголовков Access-Control-Allow-Origin от сервера ElevenLabs
  2. Региональные блокировки – некоторые страны (включая Россию) не имеют прямого доступа к api.elevenlabs.io

1.2 Решение

Создание промежуточного прокси-сервера на собственном VPS, который:

  • Принимает все запросы от виджета (HTTP/HTTPS + WebSocket)
  • Добавляет API ключ ElevenLabs на стороне сервера
  • Проксирует запросы на официальный API ElevenLabs
  • Добавляет необходимые CORS заголовки в ответы
  • Отдаёт модифицированный виджет с подменёнными URL

1.3 Почему Node.js?

Node.js выбран по следующим причинам:

  1. Нативная поддержка WebSocket – критично для голосового общения
  2. Асинхронность – эффективная обработка множества одновременных соединений
  3. Богатая экосистема – готовые библиотеки для прокси (http-proxy, axios)
  4. Простота развертывания – минимальные зависимости
  5. Производительность – отлично справляется с задачами прокси

1.4 Финальная архитектура

┌─────────────────────────────────────────────────────────────┐
│                    Браузер пользователя                      │
│                     (Любая страна)                           │
└────────────────────────┬────────────────────────────────────┘
                         │ HTTPS
                         ↓
┌─────────────────────────────────────────────────────────────┐
│              agent.domain.ru (Nginx → Flask)                   │
│              - Основной сайт                                 │
│              - Отдаёт HTML с виджетом                        │
└────────────────────────┬────────────────────────────────────┘
                         │ HTTPS + WSS
                         ↓
┌─────────────────────────────────────────────────────────────┐
│         eleven.domain.ru (Nginx → Node.js на порту 3000)       │
│         - Прокси-сервер для ElevenLabs API                   │
│         - HTTP/HTTPS прокси для REST запросов                │
│         - WebSocket прокси для аудио стриминга               │
│         - Добавляет API ключ в заголовки                     │
│         - Добавляет CORS заголовки                           │
└────────────────────────┬────────────────────────────────────┘
                         │ HTTPS + WSS + API Key
                         ↓
┌─────────────────────────────────────────────────────────────┐
│              api.elevenlabs.io                               │
│              - Официальный API ElevenLabs                    │
└─────────────────────────────────────────────────────────────┘

1.5 Что происходит при вызове

1. Загрузка виджета:

  • Браузер → unpkg.com → скачивается оригинальный JS виджета
  • JavaScript перехватчики подменяют URL API на eleven.domain.ru

2. HTTP запросы (получение данных агента, голосов):

  • Браузер → eleven.domain.ru/v1/...
  • Node.js получает запрос → добавляет xi-api-key → отправляет на api.elevenlabs.io/v1/...
  • Ответ от ElevenLabs → Node.js добавляет CORS → отправляет браузеру

3. WebSocket соединение (голосовое общение):

  • Браузер → wss://eleven.domain.ru/v1/convai/conversation
  • Nginx → WebSocket upgrade → Node.js
  • Node.js → добавляет xi-api-key → устанавливает WebSocket к wss://api.elevenlabs.io
  • Двусторонний аудио-стриминг через прокси

Часть 2: Подготовка сервера

2.1 Требования

Минимальные системные требования:

  • Ubuntu 20.04+ или Debian 11+
  • 1 CPU core
  • 1 GB RAM
  • 10 GB disk
  • Стабильное интернет-соединение

Необходимое ПО:

  • Node.js 18+
  • Nginx
  • Certbot (для SSL)
  • systemd (для автозапуска сервисов)

Доменные имена:

  • Два поддомена, указывающих на IP вашего VPS
  • Например: agent.example.com и eleven.example.com

2.2 Подключение к серверу

ssh root@YOUR_SERVER_IP
# или
ssh username@YOUR_SERVER_IP

2.3 Обновление системы

sudo apt update
sudo apt upgrade -y

2.4 Установка Node.js

# Добавляем официальный репозиторий NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -

# Устанавливаем Node.js
sudo apt install -y nodejs

# Проверяем версии
node --version  # Должно быть v20.x.x
npm --version   # Должно быть v10.x.x

2.5 Установка Nginx

sudo apt install -y nginx

# Проверяем статус
sudo systemctl status nginx

# Включаем автозапуск
sudo systemctl enable nginx

2.6 Установка Certbot (для SSL)

sudo apt install -y certbot python3-certbot-nginx

2.7 Настройка firewall

# Устанавливаем ufw если нет
sudo apt install -y ufw

# Разрешаем SSH (ВАЖНО! Иначе потеряете доступ)
sudo ufw allow 22/tcp

# Разрешаем HTTP и HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Включаем firewall
sudo ufw enable

# Проверяем статус
sudo ufw status

Часть 3: Настройка доменов и SSL

3.1 Настройка DNS записей

В панели управления вашего доменного регистратора создайте A-записи:

ТипИмяЗначениеTTL
AagentYOUR_SERVER_IP3600
AelevenYOUR_SERVER_IP3600

Проверка DNS:

# Дождитесь распространения DNS (до 24 часов, обычно 5-30 минут)
dig agent.domain.ru
dig eleven.domain.ru
Или
nslookup agent.domain.ru
nslookup eleven.domain.ru

3.2 Получение SSL сертификатов

Для первого домена (основной сайт):

sudo certbot certonly --standalone -d agent.domain.ru
Следуйте инструкциям:
- Введите email для уведомлений
- Согласитесь с условиями (Y)
- Откажитесь от рассылки (N)

Для второго домена (прокси):

sudo certbot certonly --standalone -d eleven.domain.ru

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

  • /etc/letsencrypt/live/agent.domain.ru/fullchain.pem
  • /etc/letsencrypt/live/agent.domain.ru/privkey.pem
  • /etc/letsencrypt/live/eleven.domain.ru/fullchain.pem
  • /etc/letsencrypt/live/eleven.domain.ru/privkey.pem

Автоматическое обновление сертификатов:

# Certbot автоматически создаёт cron задачу
# Проверим что обновление работает:
sudo certbot renew --dry-run

Часть 4: Настройка Nginx

4.1 Конфигурация для основного сайта (agent.domain.ru)

sudo nano /etc/nginx/sites-available/agent.domain.ru

Содержимое файла:

# HTTP → HTTPS редирект
server {
    listen 80;
    listen [::]:80;
server_name agent.domain.ru;

# Редирект всех HTTP запросов на HTTPS
return 301 https://$host$request_uri;
}
HTTPS сервер
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name agent.domain.ru;

# SSL сертификаты
ssl_certificate /etc/letsencrypt/live/agent.domain.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/agent.domain.ru/privkey.pem;

# SSL параметры безопасности
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

# Проксирование на Flask приложение
location / {
    proxy_pass http://127.0.0.1:8668;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket поддержка (если нужна)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # Таймауты
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
}
}

4.2 Конфигурация для прокси (eleven.domain.ru)

sudo nano /etc/nginx/sites-available/elevenlabs-proxy

Содержимое файла:

# HTTP → HTTPS редирект
server {
    listen 80;
    listen [::]:80;
server_name eleven.domain.ru;

return 301 https://$host$request_uri;
}
HTTPS сервер
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name eleven.domain.ru;

# SSL сертификаты
ssl_certificate /etc/letsencrypt/live/eleven.domain.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/eleven.domain.ru/privkey.pem;

# SSL параметры
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

# Проксирование на Node.js
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # КРИТИЧНО: WebSocket поддержка для голосового общения
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # Увеличенные таймауты для длительных WebSocket соединений
    proxy_connect_timeout 7d;
    proxy_send_timeout 7d;
    proxy_read_timeout 7d;

    # Буферизация
    proxy_buffering off;
}
}

4.3 Активация конфигураций

# Создаём символические ссылки
sudo ln -s /etc/nginx/sites-available/agent.domain.ru /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/elevenlabs-proxy /etc/nginx/sites-enabled/

# Удаляем дефолтную конфигурацию (опционально)
sudo rm /etc/nginx/sites-enabled/default

# Проверяем конфигурацию на ошибки
sudo nginx -t

# Если всё OK, перезагружаем Nginx
sudo systemctl reload nginx

# Проверяем статус
sudo systemctl status nginx

Ожидаемый вывод nginx -t:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Часть 5: Настройка Node.js прокси-сервера

5.1 Создание структуры проекта

# Создаём директорию для проекта
sudo mkdir -p /var/www/eleven
cd /var/www/eleven

# Создаём директорию для статических файлов (если нужно)
sudo mkdir -p /var/www/eleven/convai-widget

# Устанавливаем права доступа
sudo chown -R $USER:$USER /var/www/eleven

5.2 Инициализация Node.js проекта

cd /var/www/eleven

# Создаём package.json
npm init -y

5.3 Установка необходимых пакетов

cd /var/www/eleven

# Устанавливаем зависимости
npm install express axios cors http-proxy

# Проверяем установку
ls node_modules/

Установленные пакеты и их назначение:

  • express – веб-фреймворк для создания HTTP сервера
  • axios – HTTP клиент для проксирования REST запросов
  • cors – middleware для управления CORS заголовками
  • http-proxy – библиотека для проксирования WebSocket соединений

5.4 Создание файла прокси-сервера

nano /var/www/eleven/proxy-server.js

Полный код proxy-server.js:

const express = require('express');
const axios = require('axios');
const cors = require('cors');
const http = require('http');
const httpProxy = require('http-proxy');

// Конфигурация
const app = express();
const PORT = 3000;
const API_KEY = 'ВАШ_API_КЛЮЧ_ELEVENLABS'; // ЗАМЕНИТЕ НА СВОЙ!

// ============================================
// Middleware
// ============================================

// CORS для всех запросов
app.use(cors({ 
  origin: '*',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
}));

// Парсинг JSON
app.use(express.json());

// ============================================
// Статические файлы (опционально)
// ============================================

// Если хотите отдавать модифицированный виджет
app.use('/convai-widget', express.static('/var/www/eleven/convai-widget'));

// ============================================
// Тестовый endpoint
// ============================================

app.get('/test', (req, res) => {
  res.json({ 
    status: 'ok', 
    message: 'ElevenLabs Proxy is running',
    timestamp: new Date().toISOString()
  });
});

// ============================================
// HTTP прокси для REST API запросов
// ============================================

app.use('/v1', async (req, res) => {
  // Формируем полный URL
  const url = `https://api.elevenlabs.io${req.originalUrl}`;

  console.log(`→ HTTP ${req.method} ${url}`);

  try {
    // Конфигурация запроса
    const config = {
      method: req.method,
      url: url,
      headers: {
        'xi-api-key': API_KEY,
        'Content-Type': req.headers['content-type'] || 'application/json',
        'User-Agent': 'ElevenLabs-Proxy/1.0'
      },
      responseType: 'stream',
      validateStatus: () => true
    };

    // Добавляем тело запроса для POST/PUT
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      config.data = req.body;
    }

    // Выполняем запрос к ElevenLabs API
    const response = await axios(config);

    console.log(`✓ Response: ${response.status}`);

    // Копируем заголовки ответа
    Object.keys(response.headers).forEach(key => {
      res.set(key, response.headers[key]);
    });

    // Добавляем CORS заголовки
    res.set('Access-Control-Allow-Origin', '*');
    res.set('Access-Control-Allow-Credentials', 'true');

    // Устанавливаем статус код
    res.status(response.status);

    // Стримим данные обратно клиенту
    response.data.pipe(res);

  } catch (error) {
    console.error(`✗ Error: ${error.message}`);
    res.status(500).json({ 
      error: 'Proxy error',
      message: error.message 
    });
  }
});

// ============================================
// HTTP сервер
// ============================================

const server = http.createServer(app);

// ============================================
// WebSocket прокси для голосового общения
// ============================================

const wsProxy = httpProxy.createProxyServer({
  target: 'wss://api.elevenlabs.io',
  ws: true,
  changeOrigin: true,
  secure: true
});

// Обработка ошибок WebSocket прокси
wsProxy.on('error', (err, req, socket) => {
  console.error(`✗ WebSocket proxy error: ${err.message}`);
  if (socket.writable) {
    socket.end();
  }
});

// Обработка WebSocket upgrade
server.on('upgrade', (req, socket, head) => {
  console.log(`→ WebSocket upgrade: ${req.url}`);

  // Добавляем API ключ в заголовки
  req.headers['xi-api-key'] = API_KEY;

  // Проксируем WebSocket соединение
  wsProxy.ws(req, socket, head, {
    target: 'wss://api.elevenlabs.io'
  }, (err) => {
    if (err) {
      console.error(`✗ WebSocket upgrade error: ${err.message}`);
    }
  });
});

// ============================================
// Запуск сервера
// ============================================

server.listen(PORT, '127.0.0.1', () => {
  console.log('='.repeat(50));
  console.log(`ElevenLabs Proxy Server`);
  console.log('='.repeat(50));
  console.log(`Listening on: http://127.0.0.1:${PORT}`);
  console.log(`HTTP/HTTPS: /v1/*`);
  console.log(`WebSocket: wss://`);
  console.log('='.repeat(50));
});

// Обработка ошибок сервера
server.on('error', (err) => {
  console.error(`Server error: ${err.message}`);
  process.exit(1);
});

// ============================================
// Graceful shutdown
// ============================================

process.on('SIGTERM', () => {
  console.log('SIGTERM received, closing server...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  console.log('SIGINT received, closing server...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

⚠️ ВАЖНО: Замените API_KEY!
Найдите строку const API_KEY = 'ВАШ_API_КЛЮЧ_ELEVENLABS'; и замените на ваш реальный API ключ от ElevenLabs.

5.5 Тестирование прокси-сервера

Запуск вручную для проверки:

cd /var/www/eleven
node proxy-server.js

В другом терминале проверьте работу:

# Тест endpoint
curl http://127.0.0.1:3000/test
Тест API voices
curl http://127.0.0.1:3000/v1/voices

Если всё работает – нажмите Ctrl+C чтобы остановить сервер.

5.6 Создание systemd сервиса

sudo nano /etc/systemd/system/eleven-proxy.service

Содержимое файла:

[Unit]
Description=ElevenLabs Proxy Server
Documentation=https://docs.elevenlabs.io
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/eleven
ExecStart=/usr/bin/node /var/www/eleven/proxy-server.js
Автоматический перезапуск при падении
Restart=on-failure
RestartSec=5
Логирование
StandardOutput=journal
StandardError=journal
SyslogIdentifier=eleven-proxy
Переменные окружения
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target

Установка прав доступа:

# Устанавливаем владельца директории
sudo chown -R www-data:www-data /var/www/eleven
Устанавливаем права на файлы
sudo chmod 755 /var/www/eleven
sudo chmod 644 /var/www/eleven/proxy-server.js

Активация и запуск сервиса:

# Перезагружаем конфигурацию systemd
sudo systemctl daemon-reload
Включаем автозапуск при старте системы
sudo systemctl enable eleven-proxy
Запускаем сервис
sudo systemctl start eleven-proxy
Проверяем статус
sudo systemctl status eleven-proxy

Часть 6: Настройка Flask приложения

6.1 Структура Flask приложения

# Создаём директорию
sudo mkdir -p /home/$USER/agent
cd /home/$USER/agent

6.2 Простой Flask сервер

nano /home/$USER/agent/app.py

Минимальный app.py:

from flask import Flask, render_template_string
app = Flask(name)
HTML шаблон будет добавлен в следующей части
HTML_TEMPLATE = """
<!-- HTML код будет здесь -->
"""
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
if name == 'main':
app.run(host='0.0.0.0', port=8668, debug=False)

6.3 Создание systemd сервиса для Flask

sudo nano /etc/systemd/system/agent-flask.service

Содержимое (замените $USER на ваше имя):

[Unit]
Description=Agent Flask Application
After=network.target
[Service]
Type=simple
User=$USER
WorkingDirectory=/home/$USER/agent
ExecStart=/usr/bin/python3 /home/$USER/agent/app.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

Активация сервиса:

sudo systemctl daemon-reload
sudo systemctl enable agent-flask
sudo systemctl start agent-flask
sudo systemctl status agent-flask

Часть 7: HTML код виджета с перехватом запросов

7.1 Финальный рабочий HTML

Вставьте этот код в переменную HTML_TEMPLATE в app.py:

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Мудрый советник</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
text-align: center;
padding: 50px 16px;
background: #ffffff;
margin: 0;
}
h1 {
font-size: 28px;
margin-bottom: 10px;
}
p {
font-size: 18px;
margin-bottom: 40px;
}

/* Обёртка виджета */
.widget-wrapper {
position: relative;
margin: 0 auto;
max-width: 360px;
width: 100%;
min-height: 320px;
}

/* Маска для скрытия "Powered by ElevenLabs" */
.powered-mask {
position: absolute;
bottom: 0;
right: 0;
left: 0;
width: 100%;
height: 50px;
background: #ffffff;
z-index: 999;
pointer-events: none;
}

/* Адаптивность */
@media (max-width: 400px) {
body {
padding: 40px 12px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
}
</style>
</head>
<body>
<h1>Мудрый советник</h1>
<p>Нажми кнопку, чтобы поговорить:</p>

<!-- КРИТИЧНО: Перехват запросов ПЕРЕД загрузкой виджета -->
<script>
(function() {
console.log('Setting up proxy intercepts...');

// Перехват fetch для HTTP запросов
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (typeof url === 'string') {
const originalUrl = url;
// Подменяем URL на наш прокси
url = url.replace('https://api.elevenlabs.io', 'https://eleven.t-ip.ru');
url = url.replace('https://api.us.elevenlabs.io', 'https://eleven.t-ip.ru');
if (originalUrl !== url) {
console.log('✓ Fetch redirected');
}
}
return originalFetch.call(this, url, options);
};

// Перехват WebSocket для голосового общения
const OriginalWebSocket = window.WebSocket;
window.WebSocket = function(url, protocols) {
if (typeof url === 'string') {
const originalUrl = url;
// Подменяем WebSocket URL на наш прокси
url = url.replace('wss://api.elevenlabs.io', 'wss://eleven.t-ip.ru');
url = url.replace('wss://api.us.elevenlabs.io', 'wss://eleven.t-ip.ru');
url = url.replace('ws://api.elevenlabs.io', 'ws://eleven.t-ip.ru');
url = url.replace('ws://api.us.elevenlabs.io', 'ws://eleven.t-ip.ru');

if (originalUrl !== url) {
console.log('✓ WebSocket redirected');
}
}
return new OriginalWebSocket(url, protocols);
};

// Копируем статические свойства WebSocket
Object.keys(OriginalWebSocket).forEach(key => {
window.WebSocket[key] = OriginalWebSocket[key];
});

console.log('✓ Proxy intercepts ready!');
})();
</script>

<!-- Виджет -->
<div class="widget-wrapper">
<elevenlabs-convai agent-id="6801k7pb4wcfe1q941kprbdpwsqr"></elevenlabs-convai>
<div class="powered-mask"></div>
</div>

<!-- Загрузка виджета с unpkg.com -->
<script src="https://unpkg.com/@elevenlabs/convai-widget-embed" async type="text/javascript"></script>
</body>
</html>

⚠️ Важно: Замените 6801k7pb4wcfe1q941kprbdpwsqr на ваш реальный Agent ID из панели ElevenLabs.

Часть 8: Принцип работы перехвата запросов

8.1 Как работает перехват fetch()

Теория: window.fetch() – это встроенная браузерная функция для HTTP запросов. Мы переопределяем её, сохраняя оригинал:

// 1. Сохраняем оригинальную функцию
const originalFetch = window.fetch;

// 2. Создаём свою обёртку
window.fetch = function(url, options) {
  // 3. Изменяем URL если нужно
  if (typeof url === 'string') {
    url = url.replace('https://api.elevenlabs.io', 'https://eleven.domain.ru');
  }

  // 4. Вызываем оригинальную функцию с изменённым URL
  return originalFetch.call(this, url, options);
};

Что происходит:

  1. Виджет вызывает fetch('https://api.elevenlabs.io/v1/voices')
  2. Наша обёртка перехватывает вызов
  3. URL заменяется на https://eleven.domain.ru/v1/voices
  4. Запрос идёт на наш прокси вместо ElevenLabs
  5. Node.js получает запрос, добавляет API ключ
  6. Node.js отправляет на реальный ElevenLabs API
  7. Ответ возвращается обратно через всю цепочку

8.2 Как работает перехват WebSocket

Теория: WebSocket – это протокол для двусторонней связи в реальном времени. Принцип перехвата аналогичен fetch:

// 1. Сохраняем оригинальный конструктор
const OriginalWebSocket = window.WebSocket;

// 2. Создаём свой конструктор
window.WebSocket = function(url, protocols) {
  // 3. Изменяем URL
  if (typeof url === 'string') {
    url = url.replace('wss://api.elevenlabs.io', 'wss://eleven.domain.ru');
  }

  // 4. Создаём WebSocket с новым URL
  return new OriginalWebSocket(url, protocols);
};

// 5. ВАЖНО: Копируем статические свойства
Object.keys(OriginalWebSocket).forEach(key => {
  window.WebSocket[key] = OriginalWebSocket[key];
});

Поток данных при голосовом вызове:

  1. Пользователь нажимает “Start a call”
  2. Виджет пытается открыть WebSocket: wss://api.elevenlabs.io/v1/convai/conversation
  3. Наш перехватчик меняет URL на: wss://eleven.domain.ru/v1/convai/conversation
  4. Браузер → Nginx (eleven.domain.ru)
  5. Nginx видит WebSocket Upgrade → проксирует на Node.js (порт 3000)
  6. Node.js получает Upgrade запрос
  7. Node.js добавляет заголовок ‘xi-api-key’
  8. Node.js устанавливает WebSocket соединение с реальным ElevenLabs API
  9. Двусторонняя передача аудио-данных через прокси

8.3 Почему это работает

Ключевые моменты:

  1. JavaScript выполняется ПЕРЕД загрузкой виджета – перехватчики устанавливаются до того как виджет попытается сделать запросы
  2. Виджет не знает о подмене – для него это выглядит как обычные вызовы fetch() и WebSocket()
  3. Прокси прозрачный – добавляет только API ключ и CORS заголовки, не изменяя данные
  4. WebSocket проходит через всю цепочку – Nginx правильно обрабатывает Upgrade, Node.js корректно проксирует бинарные данные

Часть 9: Проверка и отладка

9.1 Проверка инфраструктуры

Шаг 1: Проверка DNS

dig agent.domain.ru +short
dig eleven.domain.ru +short
# Оба должны вернуть IP вашего сервера

Шаг 2: Проверка SSL сертификатов

curl -I https://agent.domain.ru
curl -I https://eleven.domain.ru/test
# Не должно быть ошибок SSL

Шаг 3: Проверка сервисов

sudo systemctl status nginx
sudo systemctl status eleven-proxy
sudo systemctl status agent-flask
# Все должны быть active (running)

Шаг 4: Проверка портов

sudo ss -tlnp | grep -E ':(80|443|3000|8668)'
# :80 - nginx, :443 - nginx, :3000 - node, :8668 - python3

9.2 Проверка Node.js прокси

# Health check
curl http://127.0.0.1:3000/test

# API voices
curl http://127.0.0.1:3000/v1/voices | jq '.'

# Через домен
curl https://eleven.domain.ru/test
curl https://eleven.domain.ru/v1/voices

9.3 Проверка в браузере

Откройте https://agent.domain.ru и откройте DevTools (F12):

1. Вкладка Console – должны появиться:

Setting up proxy intercepts...
✓ Proxy intercepts ready!
✓ Fetch redirected
✓ WebSocket redirected

2. Вкладка Network – все запросы на eleven.domain.ru:

  • Fetch/XHR: запросы к eleven.domain.ru/v1/...
  • WS: WebSocket к eleven.domain.ru/v1/convai/conversation

9.4 Просмотр логов

# Логи Node.js прокси
sudo journalctl -u eleven-proxy -f

# Логи Flask
sudo journalctl -u agent-flask -f

# Логи Nginx
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

Часть 10: Troubleshooting (Решение проблем)

10.1 Проблема: 502 Bad Gateway

Причина: Node.js прокси не запущен или недоступен.

Решение:

sudo systemctl status eleven-proxy
sudo systemctl start eleven-proxy
sudo journalctl -u eleven-proxy -n 50 --no-pager
sudo ss -tlnp | grep 3000

10.2 Проблема: CORS ошибки

Причина: Node.js не добавляет CORS заголовки.

Решение: Проверьте что в proxy-server.js есть:

res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Credentials', 'true');
app.use(cors({ origin: '*', credentials: true }));

10.3 Проблема: WebSocket не подключается

Причина: Node.js не обрабатывает WebSocket Upgrade правильно.

Решение:

  1. Проверьте Nginx конфиг – должны быть строки для WebSocket
  2. Проверьте Node.js – должен быть обработчик server.on('upgrade')
  3. Перезапустите сервисы

10.4 Проблема: 404 Not Found

Причина: Неправильная конструкция URL в Node.js.

Решение: Убедитесь что используется req.originalUrl:

const url = `https://api.elevenlabs.io${req.originalUrl}`;

10.5 Проблема: Высокая задержка

Причина: Прокси добавляет hop, медленная сеть.

Решение:

  • Используйте сервер близко к пользователям
  • Проверьте ping api.elevenlabs.io
  • Оптимизируйте Node.js конфиг

Часть 11: Безопасность

11.1 Защита API ключа

КРИТИЧНО: API ключ ElevenLabs должен быть на сервере, НИКОГДА в браузере!

Дополнительная защита – переменные окружения:

# Создаём .env файл
sudo nano /var/www/eleven/.env
Содержимое:
ELEVENLABS_API_KEY=sk_ваш_ключ
NODE_ENV=production
PORT=3000
Защищаем файл
sudo chmod 600 /var/www/eleven/.env
sudo chown www-data:www-data /var/www/eleven/.env
В proxy-server.js:
require('dotenv').config();
const API_KEY = process.env.ELEVENLABS_API_KEY;

11.2 Rate limiting

npm install express-rate-limit

const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: 'Too many requests'
});
app.use('/v1', limiter);

11.3 Fail2Ban

sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Часть 12: Оптимизация и масштабирование

12.1 Кэширование в Nginx

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

12.2 Сжатие Gzip

# В /etc/nginx/nginx.conf
http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/javascript;
}

12.3 PM2 для Node.js

sudo npm install -g pm2
pm2 start proxy-server.js --name eleven-proxy
pm2 startup
pm2 save
pm2 monit

Часть 13: Резюме и чек-лист

13.1 Что мы настроили

  • VPS сервер с Ubuntu
  • Два домена с SSL сертификатами
  • Nginx как reverse proxy
  • Node.js прокси-сервер для ElevenLabs API
  • Flask приложение для основного сайта
  • HTML виджет с перехватом запросов
  • Systemd сервисы для автозапуска
  • Логирование и мониторинг

13.2 Чек-лист для нового сервера

Подготовка:

  • Получить VPS с Ubuntu 20.04+
  • Настроить DNS A-записи для двух доменов
  • Подключиться по SSH

Установка:

  • Обновить систему: apt update && apt upgrade
  • Установить Node.js 20+
  • Установить Nginx
  • Установить Certbot
  • Настроить firewall (ufw)

SSL сертификаты:

  • Получить SSL для agent.domain.com
  • Получить SSL для eleven.domain.com

Nginx:

  • Создать конфиг для agent.domain.com
  • Создать конфиг для eleven.domain.com
  • Активировать конфиги
  • Проверить: nginx -t
  • Перезагрузить: systemctl reload nginx

Node.js прокси:

  • Создать директорию /var/www/eleven
  • Создать package.json
  • Установить пакеты
  • Создать proxy-server.js
  • Заменить API_KEY на свой!
  • Протестировать вручную
  • Создать systemd сервис
  • Запустить и включить автозапуск

Flask приложение:

  • Создать app.py
  • Вставить HTML код с виджетом
  • Заменить agent-id на свой!
  • Создать systemd сервис
  • Запустить и включить автозапуск

Проверка:

  • Открыть https://agent.domain.com
  • Проверить Console на перехват
  • Проверить Network на запросы
  • Нажать “Start a call”
  • Проверить голосовое общение

Заключение

Теперь у вас есть полнофункциональная инфраструктура для работы с ElevenLabs ConvAI виджетом:

  • ✅ Работает из любой страны без VPN
  • ✅ API ключ защищён на сервере
  • ✅ Нет CORS ошибок
  • ✅ Полная поддержка WebSocket
  • ✅ Автоматический запуск сервисов
  • ✅ SSL сертификаты
  • ✅ Логирование

Этот мануал можно использовать для развёртывания на любом другом VPS сервере. Просто следуйте инструкциям шаг за шагом и заменяйте домены, API ключи и agent-id на свои.

Удачи! 🚀
© 2025 | Версия 1.0

Понравилась статья? Поделиться с друзьями: