Security and Deployment
Secure your API, manage keys, and deploy to production safely.

Security and Deployment
Running open-wa in production requires attention to API key management, network security, and session protection.
Runtime prerequisites
For monorepo development and source builds, the repo declares Node.js >= 22.21.1 and pnpm >= 10.25.0, with packageManager pinned to pnpm@10.26.1. Match those versions before debugging runtime or build issues.
API Key Management
Why API Keys Are Required
Every open-wa session should be protected by an API key. Without one, anyone who can reach your server can control your WhatsApp session.
Generating Secure Keys
Use a cryptographically random string:
# Generate a random key
openssl rand -hex 32
# or
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Storing Keys Safely
Environment variables are the recommended approach:
export WA_API_KEY="your-generated-key"
npx @open-wa/wa-automate --port 8080 --api-key "$WA_API_KEY"Or in a .env file:
WA_API_KEY=your-generated-keyFor production deployments, use a secrets manager (AWS Secrets Manager, HashiCorp Vault, Doppler, etc.).
Rotating Keys Without Downtime
- Start a new session with the new API key
- Update all consumers to use the new key
- Stop the old session
- The session data (auth tokens) is separate from the API key, so rotation does not require re-authentication
What Happens If a Key Is Leaked
If your API key is exposed:
- Stop the running session immediately
- Generate a new API key
- Restart with the new key
- Audit what actions were taken with the leaked key
- Consider logging out from the phone and re-authenticating if you suspect unauthorized access
Port and Network Security
Exposing Ports to the Internet
Never expose the Easy API port directly to the internet without authentication. At minimum:
- Always use
--api-key "your-secure-key" - Use a reverse proxy (nginx, Caddy) with HTTPS
- Restrict access to known IP addresses when possible
Using Reverse Proxies
nginx example:
server {
listen 443 ssl;
server_name whatsapp-api.example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Caddy example:
whatsapp-api.example.com {
reverse_proxy localhost:8080
}Cloudflare Tunnel Alternative
Instead of opening ports, use Cloudflare Tunnel to expose your API securely:
cloudflared tunnel --url http://localhost:8080This gives you HTTPS without managing certificates or opening firewall rules.
Firewall Rules
If you must expose the API directly, restrict access:
# Only allow specific IPs
ufw allow from 1.2.3.4 to any port 8080Webhook Security
Securing Webhook Endpoints
When open-wa sends webhooks to your service:
- Use HTTPS for the webhook URL
- Add authentication headers via the webhook
headersconfig - Verify the source IP if your endpoint is public
Signature Verification
If your webhook endpoint is public, implement signature verification to ensure requests come from your open-wa instance.
Rate Limiting Webhook Receivers
Your webhook receiver should handle the volume of incoming events. The webhook plugin supports:
concurrency(default: 10): max concurrent deliveriesretries(default: 3): retry attempts on failureretryDelay(default: 1000ms): base delay with exponential backofftimeout(default: 30000ms): request timeout
MCP Security
API Key Enforcement
MCP requires an Easy API API key. It will not start without one. Every MCP request must include the same key as the Easy API through an accepted API-key header: X-API-Key, api_key, or key.
Endpoint Exposure Risks
If the MCP endpoint is exposed publicly, the API key is leaked, or untrusted agents are allowed to use the key, those agents could:
- Read all messages in all chats
- Send messages to any contact
- Access session information
Always protect the MCP endpoint with the API key, keep the key secret, and, ideally, place the endpoint behind a reverse proxy.
Restricting AI Agent Operations
Currently, MCP exposes all Easy API methods as tools. There are no permission scopes, so any authenticated agent can call any method. Plan your deployment accordingly.
Audit Trails
Monitor MCP usage through:
- API access logs
- The dashboard MCP page (
http://localhost:8080/dashboard/mcp) - Your reverse proxy access logs
Proxy Security
Cloudflare Proxy Token Rotation
The Cloudflare Session Proxy uses consumer tokens for authentication. Rotate these tokens periodically:
- Update the token in your Cloudflare Worker
- Update the token in your consumer connection string
- Test the new connection before removing the old token
Custom Domains
You can attach a custom domain to your Cloudflare Worker for a cleaner connection URL.
Production Checklist
Before deploying to production:
- API key set and stored securely (not in code or config files committed to git)
- HTTPS enabled (reverse proxy or Cloudflare Tunnel)
- Session persistence configured (volumes for Docker, persistent disk for servers)
- Webhook auth configured (headers or IP restriction)
- Rate limits understood and safe defaults applied
- Logging enabled and monitored
- Backup strategy for session files in place
- Monitoring configured (health checks, alerting on session disconnect)
- Process manager configured (PM2, systemd, Docker restart policy)
- Firewall rules applied (only necessary ports open)
Deployment Topologies
Single Server
[Your App] ←→ [open-wa Easy API] ←→ WhatsApp Web (browser)- Simplest setup
- Run with
npx @open-wa/wa-automate --port 8080 --api-key "key" - Use PM2 or systemd for process management
Docker Container
docker run -p 8080:8080 \
-e WA_PORT=8080 \
-e WA_API_KEY="your-key" \
-e WA_SESSION_ID=sales \
-e WA_USER_DATA_DIR=/sessions/sales \
-v ./sessions:/sessions \
--init \
openwa/wa-automate- Isolated environment
- Volume mount plus
WA_USER_DATA_DIRfor session persistence --initfor proper process cleanup
The v5 Docker image runs from /usr/src/app. If WA_USER_DATA_DIR is not set and the session is not ephemeral, the runtime derives profile storage as /usr/src/app/_IGNORE_<sessionId>. For Docker, prefer mounting /sessions and setting WA_USER_DATA_DIR=/sessions/<sessionId> so the browser profile is on the mounted volume.
Docker Compose (Multi-Session)
version: '3'
services:
sales-bot:
image: openwa/wa-automate
ports:
- "8080:8080"
volumes:
- ./sessions:/sessions
environment:
- WA_API_KEY=sales-key
- WA_SESSION_ID=sales
- WA_PORT=8080
- WA_USER_DATA_DIR=/sessions/sales
support-bot:
image: openwa/wa-automate
ports:
- "8081:8081"
volumes:
- ./sessions:/sessions
environment:
- WA_API_KEY=support-key
- WA_SESSION_ID=support
- WA_PORT=8081
- WA_USER_DATA_DIR=/sessions/supportAfter first authentication, restart each container with the same WA_SESSION_ID and WA_USER_DATA_DIR. Expected local verification: the session reconnects without a new QR prompt because the profile directory under ./sessions/<sessionId> is reused. This is a checklist expectation for local deployment validation, not captured terminal output from this docs update.
Cloudflare Proxy + Local Runtime
[Your App] ←→ [Cloudflare Proxy] ←→ [open-wa Easy API (local)] ←→ WhatsApp- No open ports on your server
- Remote access without public exposure
- See Cloudflare Session Proxy
Kubernetes
For large-scale deployments:
- One pod per session (sessions do not share state)
- Persistent volumes for session data
- Horizontal Pod Autoscaler based on session count
- Service mesh for internal routing
Related
- Quick Start: First-time setup
- Cloudflare Session Proxy: Secure remote access
- Best Practices: Production patterns

Was this helpful?
Wally and his cute companion coffee mug are coding day and night to keep this up-to-date!
