Documentation Index
Fetch the complete documentation index at: https://elizalabs-docs-x402-deployment-guide.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
x402 Payment Integration Guide
Overview
The x402 protocol enables crypto payment requirements for API endpoints using the Coinbase Developer Platform (CDP). This guide covers how to integrate x402 payments into your ElizaOS deployment.
What is x402?
x402 is a payment protocol that allows you to monetize your API endpoints by requiring crypto payments (USDC) for each request. It works seamlessly with both testnet (for development) and mainnet (for production).
Key Benefits
- Monetize APIs: Charge per API call in USDC
- Blockchain-native: Payments are verified on-chain
- Easy integration: Simple environment variable configuration
- Flexible pricing: Set different prices per endpoint
- Testnet support: Test with Base Sepolia before going live
Quick Start
1. Get Your Wallet Address
You need an EVM-compatible wallet address to receive payments. This should be a Base or Base Sepolia address.
Important: Your wallet address must:
- Start with
0x
- Be exactly 42 characters long
- Contain only hexadecimal characters (0-9, a-f, A-F)
Example: 0x1234567890abcdef1234567890abcdef12345678
Add these variables to your .env file:
Required (when enabling x402)
X402_ENABLED=true
X402_WALLET_ADDRESS=0xYourWalletAddressHere
Optional Configuration
# Price per API call (default: $0.01)
X402_PRICE=$0.001
# Network: "base-sepolia" for testnet, "base" for mainnet (default: base-sepolia)
X402_NETWORK=base-sepolia
# Facilitator URL for testnet (default: https://x402.org/facilitator)
X402_FACILITATOR_URL=https://x402.org/facilitator
# Use mainnet facilitator (requires CDP credentials)
X402_USE_MAINNET=false
3. Testnet Setup (Recommended First)
Start with Base Sepolia testnet to test your integration:
X402_ENABLED=true
X402_WALLET_ADDRESS=0xYourSepoliaWalletAddress
X402_NETWORK=base-sepolia
X402_PRICE=$0.001
Get Testnet USDC: Use the Base Sepolia faucet to get test tokens.
4. Mainnet Setup
For production, you’ll need CDP API credentials:
Get CDP Credentials
- Sign up at Coinbase Developer Platform
- Create an API key
- Save your API Key ID and Secret
Configure Mainnet
X402_ENABLED=true
X402_USE_MAINNET=true
X402_WALLET_ADDRESS=0xYourMainnetWalletAddress
X402_NETWORK=base
X402_PRICE=$0.01
# CDP Credentials (required for mainnet)
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secret
Authentication Modes
The x402 middleware supports multiple authentication configurations:
Mode 1: x402 Only
Users pay with crypto, no API key required.
X402_ENABLED=true
X402_WALLET_ADDRESS=0xYourAddress
# ELIZA_SERVER_AUTH_TOKEN not set
Use case: Public monetized API
Mode 2: API Key Only
Traditional API key authentication, no payment required.
X402_ENABLED=false
ELIZA_SERVER_AUTH_TOKEN=your-secret-api-key
Use case: Private API for known clients
Mode 3: Both x402 + API Key
Requires BOTH valid API key AND payment.
X402_ENABLED=true
X402_WALLET_ADDRESS=0xYourAddress
ELIZA_SERVER_AUTH_TOKEN=your-secret-api-key
Use case: Restricted monetized API
Mode 4: Open Access
No authentication required (not recommended for production).
X402_ENABLED=false
# ELIZA_SERVER_AUTH_TOKEN not set
Use case: Development/testing only
Jobs API Integration
The Jobs API (POST /api/messaging/jobs) and status endpoint (GET /api/messaging/jobs/:jobId) both use x402 middleware for consistent authentication.
Per-Endpoint Configuration
You can customize pricing and metadata per endpoint:
# Jobs endpoint price (overrides X402_PRICE)
X402_JOBS_PRICE=$0.005
# Optional: Add API discovery metadata
X402_JOBS_ENDPOINT_DESCRIPTION="Submit a one-time message to an AI agent"
# Optional: Define input/output schemas for API documentation
X402_JOBS_INPUT_SCHEMA='{"type":"object","properties":{"agentId":{"type":"string"},"content":{"type":"string"}}}'
X402_JOBS_OUTPUT_SCHEMA='{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string"}}}'
Making Payments as a Client
When x402 is enabled, clients must include an X-PAYMENT header with payment proof.
Using x402 SDK (JavaScript/TypeScript)
import { Payment } from '@coinbase/x402';
// Create payment for endpoint
const payment = await Payment.create({
endpoint: 'https://your-api.com/api/messaging/jobs',
method: 'POST',
network: 'base-sepolia',
facilitatorUrl: 'https://x402.org/facilitator',
});
// Make request with payment header
const response = await fetch('https://your-api.com/api/messaging/jobs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-PAYMENT': payment.proof,
// Include API key if both auth methods are enabled
'X-API-KEY': 'your-api-key',
},
body: JSON.stringify({
agentId: 'agent-123',
content: 'Hello, agent!',
}),
});
Example: Complete Client Flow
import { Payment } from '@coinbase/x402';
async function callJobsAPI(agentId: string, content: string) {
const apiUrl = 'https://your-api.com/api/messaging/jobs';
// Step 1: Create payment
const payment = await Payment.create({
endpoint: apiUrl,
method: 'POST',
network: 'base-sepolia',
facilitatorUrl: 'https://x402.org/facilitator',
});
// Step 2: Submit job
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-PAYMENT': payment.proof,
},
body: JSON.stringify({ agentId, content }),
});
const job = await response.json();
console.log('Job created:', job.id);
// Step 3: Check job status (uses same payment method)
const statusPayment = await Payment.create({
endpoint: `${apiUrl}/${job.id}`,
method: 'GET',
network: 'base-sepolia',
facilitatorUrl: 'https://x402.org/facilitator',
});
const statusResponse = await fetch(`${apiUrl}/${job.id}`, {
headers: {
'X-PAYMENT': statusPayment.proof,
},
});
const status = await statusResponse.json();
console.log('Job status:', status);
return status;
}
Security Best Practices
1. Protect Your Wallet Private Keys
Never commit private keys to version control:
# .gitignore
.env
.env.local
.env.*.local
2. Use Different Wallets for Test/Prod
Keep separate wallets for testnet and mainnet:
# .env.testnet
X402_WALLET_ADDRESS=0xTestnetWalletAddress
X402_NETWORK=base-sepolia
# .env.production
X402_WALLET_ADDRESS=0xProductionWalletAddress
X402_NETWORK=base
X402_USE_MAINNET=true
3. Rotate CDP API Keys Regularly
Set up key rotation for your CDP credentials:
# Use secrets management
CDP_API_KEY_ID=$(vault read secret/cdp/key-id)
CDP_API_KEY_SECRET=$(vault read secret/cdp/key-secret)
4. Monitor Payment Flow
Track payments and detect anomalies:
// Set up monitoring
logger.info('[x402] Payment received', {
wallet: config.walletAddress,
network: config.network,
amount: payment.amount,
});
Troubleshooting
Error: “Invalid or missing X-PAYMENT”
Cause: Payment header is missing or invalid
Solution: Ensure client is sending valid payment proof in X-PAYMENT header
Error: “X402_WALLET_ADDRESS must start with 0x”
Cause: Wallet address format is invalid
Solution: Verify your wallet address:
- Starts with
0x
- Is exactly 42 characters
- Contains only hex characters (0-9, a-f, A-F)
Error: “Mainnet facilitator requires CDP_API_KEY_ID”
Cause: Mainnet is enabled but CDP credentials are missing
Solution: Set both CDP_API_KEY_ID and CDP_API_KEY_SECRET when using mainnet
Payment Not Received
Check:
- Wallet address is correct
- Network matches (testnet vs mainnet)
- Client is using correct facilitator URL
- USDC is available in client wallet
Testing
Unit Tests
Test your x402 configuration:
import { describe, test, expect } from 'bun:test';
import { createX402Middleware } from '../middleware/x402';
describe('x402 Configuration', () => {
test('should validate wallet address format', () => {
process.env.X402_ENABLED = 'true';
process.env.X402_WALLET_ADDRESS = 'invalid-address';
expect(() => createX402Middleware({})).toThrow('X402_WALLET_ADDRESS must start with 0x');
});
});
Integration Tests
Test the complete payment flow:
# Run with testnet configuration
elizaos test --name "x402 payment flow"
Production Deployment with Nginx
Why Use Nginx?
Nginx acts as a reverse proxy to provide:
- SSL/TLS termination: Secure HTTPS connections
- Rate limiting: Prevent abuse and DDoS attacks
- Load balancing: Distribute traffic across multiple instances
- Static file serving: Offload static content from your API
- Security headers: Add security layers
- Access control: Restrict access by IP, geography, etc.
Step 1: Install Nginx
Ubuntu/Debian
sudo apt update
sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
CentOS/RHEL
sudo yum install epel-release
sudo yum install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
macOS
brew install nginx
brew services start nginx
Step 2: Basic Nginx Configuration
Create a new configuration file for your ElizaOS API:
sudo nano /etc/nginx/sites-available/eliza-api
Add the following configuration:
# ElizaOS API - Basic Configuration
upstream eliza_backend {
# Your ElizaOS server (default port 3000)
server 127.0.0.1:3000;
# For multiple instances (load balancing):
# server 127.0.0.1:3001;
# server 127.0.0.1:3002;
}
server {
listen 80;
server_name api.yourdomain.com;
# Redirect all HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
# SSL Configuration (we'll add certificates next)
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Logging
access_log /var/log/nginx/eliza-api-access.log;
error_log /var/log/nginx/eliza-api-error.log;
# Client body size limit (for file uploads)
client_max_body_size 10M;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Proxy to ElizaOS backend
location / {
proxy_pass http://eliza_backend;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Forward headers
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;
# Preserve original request headers
proxy_pass_request_headers on;
}
# Health check endpoint (optional)
location /health {
access_log off;
proxy_pass http://eliza_backend/api/health;
}
}
Enable the configuration:
sudo ln -s /etc/nginx/sites-available/eliza-api /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl reload nginx
Step 3: SSL/TLS with Let’s Encrypt
Install Certbot
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# CentOS/RHEL
sudo yum install certbot python3-certbot-nginx
Obtain SSL Certificate
sudo certbot --nginx -d api.yourdomain.com
Follow the prompts to:
- Enter your email
- Agree to Terms of Service
- Choose whether to redirect HTTP to HTTPS (recommended: yes)
Auto-renewal
Certbot automatically sets up renewal. Test it:
sudo certbot renew --dry-run
Step 4: Rate Limiting
Protect your API from abuse with rate limiting:
# Add at the top of /etc/nginx/sites-available/eliza-api
# Define rate limit zones
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=jobs_limit:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=strict_limit:10m rate=1r/s;
# Connection limit
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
# ... existing configuration ...
# General API rate limit (10 requests/second)
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 10;
proxy_pass http://eliza_backend;
# ... other proxy settings ...
}
# Jobs API - stricter limit (5 requests/second)
location /api/messaging/jobs {
limit_req zone=jobs_limit burst=10 nodelay;
limit_conn conn_limit 5;
proxy_pass http://eliza_backend;
# ... other proxy settings ...
}
# Admin endpoints - very strict (1 request/second)
location /api/admin/ {
limit_req zone=strict_limit burst=2 nodelay;
limit_conn conn_limit 1;
# Optional: IP whitelist
allow 203.0.113.0/24; # Your office IP range
deny all;
proxy_pass http://eliza_backend;
# ... other proxy settings ...
}
}
Step 5: DDoS Protection
Enable Connection Limits
http {
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
# Limit requests per IP
limit_req_zone $binary_remote_addr zone=one:10m rate=100r/s;
server {
# Apply limits
limit_conn addr 10;
limit_req zone=one burst=200 nodelay;
# Timeout configurations
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 30s;
send_timeout 10s;
}
}
Install Fail2ban
Automatically ban IPs with suspicious behavior:
sudo apt install fail2ban
Create Nginx jail configuration:
sudo nano /etc/fail2ban/jail.d/nginx.conf
[nginx-req-limit]
enabled = true
filter = nginx-req-limit
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/nginx/*error.log
findtime = 600
bantime = 7200
maxretry = 10
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
action = iptables-multiport[name=NoAuthFailures, port="http,https", protocol=tcp]
logpath = /var/log/nginx/*error.log
findtime = 600
bantime = 7200
maxretry = 5
Create filter:
sudo nano /etc/fail2ban/filter.d/nginx-req-limit.conf
[Definition]
failregex = limiting requests, excess:.* by zone.*client: <HOST>
ignoreregex =
Start Fail2ban:
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Step 6: Geographic Restrictions (Optional)
Restrict access by country using GeoIP:
# Install GeoIP module
sudo apt install libnginx-mod-http-geoip2 geoipupdate
Configure GeoIP:
http {
# Load GeoIP database
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_country_code country iso_code;
}
# Define allowed countries
map $geoip2_country_code $allowed_country {
default no;
US yes;
CA yes;
GB yes;
# Add your allowed countries
}
server {
location /api/ {
if ($allowed_country = no) {
return 403 "Access denied from your country";
}
# ... proxy settings ...
}
}
}
Step 7: Monitoring and Logging
http {
# Custom log format with more details
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/eliza-api-access.log detailed;
}
Log Rotation
sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
Monitor with GoAccess (Real-time)
sudo apt install goaccess
# Real-time monitoring
sudo goaccess /var/log/nginx/eliza-api-access.log -o /var/www/html/report.html --log-format=COMBINED --real-time-html
Access at: https://api.yourdomain.com/report.html
Step 8: Advanced Security
IP Whitelist for Admin Endpoints
# Allow only specific IPs for admin routes
location /api/admin/ {
# Office network
allow 203.0.113.0/24;
# VPN exit IP
allow 198.51.100.42;
# Block everyone else
deny all;
proxy_pass http://eliza_backend;
}
Hide Nginx Version
http {
server_tokens off;
}
Add WAF (Web Application Firewall)
Install ModSecurity:
sudo apt install libnginx-mod-http-modsecurity
# Enable ModSecurity
sudo nano /etc/nginx/modsecurity/modsecurity.conf
Change SecRuleEngine DetectionOnly to SecRuleEngine On
Request Body Validation
location /api/messaging/jobs {
# Only allow POST
limit_except POST {
deny all;
}
# Require Content-Type header
if ($content_type !~ "application/json") {
return 415;
}
proxy_pass http://eliza_backend;
}
Step 9: High Availability Setup
Multiple ElizaOS Instances
upstream eliza_backend {
least_conn; # Load balancing method
# Multiple instances
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
# Health check
keepalive 32;
}
Run Multiple Instances with PM2
# Install PM2
npm install -g pm2
# Create ecosystem file
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [
{
name: 'eliza-api-1',
script: 'elizaos',
args: 'start',
env: {
PORT: 3000,
NODE_ENV: 'production'
}
},
{
name: 'eliza-api-2',
script: 'elizaos',
args: 'start',
env: {
PORT: 3001,
NODE_ENV: 'production'
}
},
{
name: 'eliza-api-3',
script: 'elizaos',
args: 'start',
env: {
PORT: 3002,
NODE_ENV: 'production'
}
}
]
};
EOF
# Start all instances
pm2 start ecosystem.config.js
pm2 save
pm2 startup
Step 10: Production Deployment Checklist
Before going live, verify:
Security Checklist
x402 Configuration Checklist
Environment Checklist
Testing Checklist
Complete Production Nginx Configuration
Here’s a complete, production-ready configuration:
# /etc/nginx/nginx.conf - Global settings
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
types_hash_max_size 2048;
server_tokens off;
# MIME
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# Gzip Settings
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml application/atom+xml image/svg+xml
text/x-component application/x-font-ttf font/opentype
application/vnd.ms-fontobject;
# Rate Limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=jobs:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=strict:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
# Include site configurations
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# /etc/nginx/sites-available/eliza-api - Site configuration
upstream eliza_backend {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s backup;
keepalive 32;
}
# HTTP - Redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name api.yourdomain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS - Main Configuration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.yourdomain.com;
# SSL Certificates
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/api.yourdomain.com/chain.pem;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Logging
access_log /var/log/nginx/eliza-api-access.log main buffer=32k flush=1m;
error_log /var/log/nginx/eliza-api-error.log warn;
# Client settings
client_max_body_size 10M;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 16k;
# Timeouts
client_body_timeout 12s;
client_header_timeout 12s;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
send_timeout 10s;
# Health check (no rate limit, no logging)
location /health {
access_log off;
limit_req off;
proxy_pass http://eliza_backend/api/health;
}
# Jobs API - x402 protected
location /api/messaging/jobs {
limit_req zone=jobs burst=10 nodelay;
limit_conn conn 5;
# Only allow POST and GET
limit_except GET POST {
deny all;
}
proxy_pass http://eliza_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_pass_request_headers on;
# CORS (if needed)
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, X-API-KEY, X-PAYMENT" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# General API
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_conn conn 10;
proxy_pass http://eliza_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_pass_request_headers on;
}
# Admin API - IP restricted
location /api/admin/ {
limit_req zone=strict burst=2 nodelay;
limit_conn conn 1;
# Whitelist
allow 203.0.113.0/24; # Replace with your IP
deny all;
proxy_pass http://eliza_backend;
proxy_http_version 1.1;
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;
}
# Static files (if any)
location /static/ {
alias /var/www/eliza/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
Monitoring Production
Setup Prometheus + Grafana
Monitor Nginx metrics:
# Install nginx-prometheus-exporter
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
tar -xzf nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
sudo mv nginx-prometheus-exporter /usr/local/bin/
# Create systemd service
sudo nano /etc/systemd/system/nginx-exporter.service
[Unit]
Description=Nginx Prometheus Exporter
After=network.target
[Service]
Type=simple
User=www-data
ExecStart=/usr/local/bin/nginx-prometheus-exporter -nginx.scrape-uri=http://localhost:8080/stub_status
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable nginx-exporter
sudo systemctl start nginx-exporter
Alerting
Set up alerts for:
- High error rates (> 5%)
- Response time > 2s
- Rate limit hits
- SSL certificate expiry (< 30 days)
- Disk space (> 80%)
Resources
Support
For issues and questions: