- Posted on
- • Artificial Intelligence
Artificial Intelligence Reverse Proxy Analysis
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Reverse Proxy Analysis: Control, Observe, and Optimize Your LLM Traffic
If your AI bill surprises you each month or your team loses hours debugging flaky model calls, you’re not alone. As AI adoption explodes, the simple “call the model endpoint directly” approach becomes a liability. A reverse proxy in front of your AI providers and local model servers gives you a single, battle-tested control plane to secure keys, enforce rate limits, fail over across providers, and analyze real usage. In this article, we’ll build a practical AI reverse proxy, show how to observe it from Bash, and give you steps you can apply today on any Linux distro.
Why a reverse proxy for AI is worth it
Security and governance: Centralize API key handling, restrict access, and log all calls for compliance without refactoring every client.
Reliability and portability: Fail over between cloud AI (e.g., OpenAI/Azure) and on-prem models (vLLM/Ollama) without changing clients.
Cost and performance control: Rate-limit abusers, split-traffic to compare models, and cache/short-circuit health checks.
Observability: Structured logs and metrics let you see P95 latency, error spikes, and usage by endpoint or team.
The best part: you can do this with standard Linux tools (Nginx/HAProxy/Caddy), a few config snippets, and Bash-friendly analysis.
1) Install the tooling (Nginx/HAProxy/Caddy + CLI analyzers)
You only need one proxy to start. Nginx is a common default, but HAProxy and Caddy are excellent too. We’ll also use jq and GoAccess for log analysis.
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y nginx haproxy caddy jq goaccess certbot python3-certbot-nginx
Fedora/RHEL/CentOS Stream (dnf):
# On RHEL/CentOS you may need: sudo dnf install -y epel-release
sudo dnf install -y nginx haproxy caddy jq goaccess certbot python3-certbot-nginx
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y nginx haproxy caddy jq goaccess certbot python3-certbot-nginx
Enable and start what you choose (example: Nginx):
sudo systemctl enable --now nginx
Optional: open the firewall (example using firewalld):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
2) Core Nginx setup for AI endpoints (secure, model-aware, observable)
This Nginx config does the following:
Terminates TLS (optional snippet for Let’s Encrypt shown below)
Proxies OpenAI-compatible traffic to multiple backends (cloud and local)
Adds per-key rate limiting (by Authorization header)
Produces both human-friendly and JSON logs for Bash analysis
Preps for A/B split and failover between providers
Create a site config (replace your domain and backends):
sudo mkdir -p /etc/nginx/ai.d
sudo nano /etc/nginx/ai.d/ai.conf
Paste:
# /etc/nginx/ai.d/ai.conf
# 1) Define upstreams: a cloud AI provider and a local LLM server (e.g., vLLM or Ollama proxy)
upstream openai_api {
zone openai_zone 64k;
server api.openai.com:443 max_fails=3 fail_timeout=30s;
keepalive 64;
}
upstream local_llm {
zone local_zone 64k;
# Example local server: vLLM/OpenAI-compatible on 127.0.0.1:8000
server 127.0.0.1:8000 max_fails=3 fail_timeout=10s;
keepalive 64;
}
# 2) Rate limit per API key (Authorization: Bearer ...)
# Store ~10MB of keys, limit to 60 requests/minute per key.
limit_req_zone $http_authorization zone=per_key:10m rate=60r/m;
# 3) Structured logging for analysis
# Combined format (for GoAccess)
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# JSON format (for jq and custom tooling)
log_format ai_json escape=json
'{ "time":"$time_iso8601", "remote_addr":"$remote_addr", '
'"method":"$request_method", "path":"$uri", "status":$status, '
'"bytes":$bytes_sent, "req_time":$request_time, '
'"upstream":"$upstream_addr", "upstream_time":"$upstream_response_time", '
'"request_id":"$request_id", "content_length":"$http_content_length", '
'"client":"$http_user_agent" }';
# 4) Choose a backend dynamically: 70% to cloud, 30% to local (A/B)
# We use a split to a variable that contains a full "http://upstream" string.
split_clients "$request_id$remote_addr" $ai_backend {
70% "https://openai_api";
* "http://local_llm";
}
server {
listen 80;
server_name ai.yourdomain.com;
# Let’s Encrypt HTTP-01 challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name ai.yourdomain.com;
# TLS certs (replace with your actual cert files or use certbot below)
ssl_certificate /etc/letsencrypt/live/ai.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ai.yourdomain.com/privkey.pem;
# Access logs
access_log /var/log/nginx/ai_access.log main;
access_log /var/log/nginx/ai_access.json ai_json;
error_log /var/log/nginx/ai_error.log warn;
# Security and proxy tuning
client_max_body_size 10m;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Request-ID $request_id;
# Enforce presence of Authorization header
if ($http_authorization = "") {
return 401;
}
location /v1/ {
# Per-key rate limit, bursts allowed but with no delay
limit_req zone=per_key burst=10 nodelay;
# Timeouts and retries; fail over on gateway/timeout errors
proxy_read_timeout 300s;
proxy_connect_timeout 30s;
proxy_send_timeout 300s;
proxy_next_upstream error timeout http_502 http_503 http_504;
# Choose backend; for HTTPS upstream we must pass to the upstream group with proper scheme.
# If proxying to OpenAI directly, ensure TLS SNI and ALPN:
proxy_ssl_server_name on;
# Some AI endpoints require Authorization to be passed through:
proxy_set_header Authorization $http_authorization;
# Route by A/B decision
proxy_pass $ai_backend;
}
}
Enable and test:
sudo ln -s /etc/nginx/ai.d/ai.conf /etc/nginx/conf.d/ai.conf
sudo nginx -t
sudo systemctl reload nginx
Obtain and install TLS certificate with Certbot (optional but recommended):
# Prepare webroot directory for ACME challenges
sudo mkdir -p /var/www/html
# Issue and install cert; Certbot will edit TLS paths for you
sudo certbot --nginx -d ai.yourdomain.com --redirect --agree-tos -m you@example.com
Notes:
Swap
openai_apifor any HTTPS upstream (Azure OpenAI, Anthropic via a gateway, etc.).Swap
local_llmfor vLLM/Ollama/OpenAI-compatible servers you run internally.Move the
split_clientsweights to bias traffic to the cheapest/fastest backend, then measure.
Real-world example: When a cloud outage hits, proxy_next_upstream fails over to the local LLM for 502/504 errors, so clients remain functional.
3) Alternative: HAProxy snippet for per-key rate limiting and headers
If you prefer HAProxy, this skeleton shows header-based auth enforcement and per-key throttling via stick tables:
# /etc/haproxy/haproxy.cfg (add to your frontend/backend)
global
log /dev/log local0
maxconn 50000
defaults
mode http
timeout connect 30s
timeout client 300s
timeout server 300s
option httplog
frontend ai_front
bind *:443 ssl crt /etc/letsencrypt/live/ai.yourdomain.com/fullchain.pem crt-key /etc/letsencrypt/live/ai.yourdomain.com/privkey.pem
http-response add-header X-Proxy-Request-ID %[unique-id]
acl missing_auth hdr_cnt(Authorization) eq 0
http-request deny status 401 if missing_auth
# Track per-key req rate (Authorization header used as key)
stick-table type string len 128 size 1m expire 1m store http_req_rate(60s)
http-request track-sc0 hdr(Authorization)
acl too_fast sc_http_req_rate(0) gt 60
http-request deny status 429 if too_fast
# Simple routing: 70% cloud, 30% local
use_backend openai_api if { rand(100) lt 70 }
default_backend local_llm
backend openai_api
server s1 api.openai.com:443 ssl verify none alpn h2,http/1.1
backend local_llm
server s1 127.0.0.1:8000
Reload:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
4) Analyze AI traffic from Bash: latency, error spikes, and hot endpoints
You now have two access logs from Nginx:
/var/log/nginx/ai_access.log (combined format, GoAccess-friendly)
/var/log/nginx/ai_access.json (NDJSON, jq-friendly)
Examples:
Top called endpoints:
awk '{print $7}' /var/log/nginx/ai_access.log | sort | uniq -c | sort -nr | head
Error rate in the last 5 minutes:
sudo journalctl -u nginx --since "5 min ago" | grep -c " 5[0-9][0-9] "
P95 request time from the JSON log (simple awk percentile):
cat /var/log/nginx/ai_access.json \
| jq -r '.req_time' \
| sort -n \
| awk ' {a[NR]=$1} END{ if (NR>0) {p=int(0.95*NR); if(p<1)p=1; print a[p]} }'
Requests per minute trend:
grep "$(date '+%d/%b/%Y:%H:%M')" /var/log/nginx/ai_access.log \
| awk '{print substr($4,2,17)}' \
| sort | uniq -c | tail -n 20
Interactive dashboard with GoAccess (serves a live HTML report on port 7890):
sudo goaccess /var/log/nginx/ai_access.log \
--log-format=COMBINED \
--real-time-html -o /var/www/html/ai-report.html
# Serve the report with any static server, or:
sudo python3 -m http.server 7890 --directory /var/www/html
Tip: If your upstreams add headers like X-Usage-Tokens or X-Model, you can log them by adding to the JSON log format:
"$upstream_http_x_usage_tokens"
Then slice by token usage per route/team with jq.
Log rotation (avoid giant files):
sudo nano /etc/logrotate.d/nginx-ai
/var/log/nginx/ai_access.log /var/log/nginx/ai_access.json {
daily
rotate 14
compress
missingok
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -s /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
endscript
}
5) Practical hardening and routing patterns
Lock down who can call your proxy:
- Restrict by IP/CIDR:
allow 10.0.0.0/8; allow 192.168.0.0/16; deny all;Normalize upstream timeouts for long generations (e.g., 300s) to avoid premature 504s.
Use request IDs everywhere (we added X-Request-ID) to correlate client logs and proxy logs.
Test failover: intentionally drop the cloud upstream to see traffic move to local LLM seamlessly.
Separate per-team prefixes: route /teamA/v1 to a specific upstream, apply a stricter per-key rate limit with a second limit_req_zone, and allocate budgets operationally.
Real-world example: A platform team split 10% of chat completions to a cheaper local LLM for low-priority workloads while keeping 90% on a premium provider for critical paths. They dropped average costs by 22% in the first week, then tuned the split further as latency and quality data accumulated.
Troubleshooting quick hits
401s immediately: ensure clients send Authorization: Bearer
and that your proxy doesn’t strip it. 502/504s: raise proxy_read_timeout and confirm your upstream supports streaming if you need it.
TLS SNI issues upstream: set
proxy_ssl_server_name on;(as shown) andproxy_ssl_nameif the hostname differs.CORS for browser apps: add:
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers Authorization,Content-Type; if ($request_method = OPTIONS) { return 204; }
Conclusion and next steps
Reverse proxies turn AI chaos into control: one endpoint, many providers, with security, limits, failover, and clear analytics. Start with the Nginx config above, point a single client at it, and watch your logs. In an afternoon you can:
Protect keys and stop runaway costs
Observe latency and error rates
Split and compare models without changing client code
Call to action:
Install Nginx and jq/goaccess on your distro using the commands above.
Drop in the sample config, reload, and send a test request.
Add one improvement at a time: rate limits, A/B routing, then dashboards.
Your AI stack will be faster to debug, cheaper to run, and easier to govern—without touching every application.