main
API Proxy — HTTPS Reverse Proxy
Production-ready nginx reverse proxy in Docker for forwarding requests from an internal Kubernetes cluster to external APIs (ElevenLabs, OpenAI, Telegram Bot API).
Quick Start
Prerequisites
- Ubuntu 24.04 VM
- Port 8080 (HTTP) and optionally 8443 (HTTPS) open only to k8s cluster CIDR
Installation
sudo bash scripts/install.sh
Configuration
vi .env
# Set your secret and cluster CIDR:
PROXY_SECRET=<generated-token>
ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12
docker compose up -d --build
Verify
# Health check (no auth):
curl http://localhost:8080/health
# Test with auth:
curl -H "X-Proxy-Token: YOUR_SECRET" http://localhost:8080/elevenlabs/v1/voices
Available Endpoints
| Prefix | Upstream |
|---|---|
| /elevenlabs/ | https://api.elevenlabs.io |
| /openai/ | https://api.openai.com |
| /telegram/ | https://api.telegram.org |
| /health | local health check (no auth) |
TLS / HTTPS
To enable HTTPS on port 8443, place your certificate and key in the ssl/ directory:
mkdir -p ssl
# Option A: self-signed cert
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ssl/tls.key \
-out ssl/tls.crt \
-subj "/CN=api-proxy"
# Option B: copy existing certs
cp /path/to/your/cert.pem ssl/tls.crt
cp /path/to/your/key.pem ssl/tls.key
docker compose up -d --build
The entrypoint auto-detects ssl/tls.crt and ssl/tls.key. If present — HTTPS is enabled on port 8443. If absent — only HTTP on 8080.
How to Add a New Upstream
Example: adding api.anthropic.com → /anthropic/*
Add this block to nginx/locations.conf:
location /anthropic/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
set $anthropic_upstream https://api.anthropic.com;
rewrite ^/anthropic/(.*) /$1 break;
proxy_pass $anthropic_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.anthropic.com;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host api.anthropic.com;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-For "";
proxy_set_header X-Real-IP "";
proxy_set_header True-Client-IP "";
proxy_set_header CF-Connecting-IP "";
proxy_set_header X-Client-IP "";
proxy_set_header Forwarded "";
proxy_set_header Via "";
proxy_set_header X-Proxy-Token "";
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
Then rebuild: docker compose up -d --build
K8s Side: How to Call the Proxy
Environment Variables
env:
- name: PROXY_BASE_URL
value: "http://10.0.1.50:8080" # or https://10.0.1.50:8443
- name: PROXY_SECRET
valueFrom:
secretKeyRef:
name: api-proxy
key: token
TypeScript Examples
// ElevenLabs TTS
const response = await fetch(
`${process.env.PROXY_BASE_URL}/elevenlabs/v1/text-to-speech/${voiceId}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'xi-api-key': process.env.ELEVENLABS_API_KEY,
'X-Proxy-Token': process.env.PROXY_SECRET,
},
body: JSON.stringify(payload),
}
);
const buffer = Buffer.from(await response.arrayBuffer());
// Telegram Bot API
const tgResponse = await fetch(
`${process.env.PROXY_BASE_URL}/telegram/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Proxy-Token': process.env.PROXY_SECRET,
},
body: JSON.stringify({ chat_id: chatId, text: message }),
}
);
Security Checklist
PROXY_SECRETgenerated viaopenssl rand -hex 32ALLOWED_CIDRrestricted to cluster CIDR only- Port 8080/8443 closed to the public (only k8s CIDR in firewall/NSG)
- VM has no public IP or is behind NAT
- Logs rotate (Docker logging driver configured: 50m x 5 files)
- TLS certs have restricted permissions (
chmod 600 ssl/tls.key)
Monitoring
# Tail logs
docker compose logs -f proxy | jq .
# Requests per upstream per minute
docker compose logs --since=1m proxy --no-log-prefix \
| jq -r '.uri' \
| cut -d'/' -f2 \
| sort | uniq -c | sort -rn
# Container health
docker inspect --format='{{.State.Health.Status}}' api-proxy
How It Works
- A k8s pod sends an HTTP request to
http://<proxy-vm>:8080/elevenlabs/v1/text-to-speech/...with theX-Proxy-Tokenheader and the original API key. - Nginx checks the source IP against
ALLOWED_CIDRand validatesX-Proxy-Token— rejecting with 403 if either fails. - The prefix (
/elevenlabs/,/openai/,/telegram/) is stripped via rewrite, and the request is forwarded over HTTPS to the upstream with SNI enabled. X-Proxy-Tokenand all IP-leaking headers are removed before forwarding; API keys pass through unchanged.- The response streams back unbuffered to the pod — critical for binary audio data and long-polling (Telegram).
Languages
Shell
88.1%
Dockerfile
11.9%