# How to Secure FreeLLMAPI When Exposing It on a Network

> Learn how to secure FreeLLMAPI on your network. Implement AES-256-GCM encryption, localhost binding, bcrypt authentication, and TLS termination to protect your API.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: how-to-guide
- Published: 2026-06-24

---

**Secure FreeLLMAPI by encrypting provider API keys with AES-256-GCM, binding the service to localhost by default, enforcing dashboard authentication with bcrypt-hashed passwords, and terminating TLS through a reverse proxy.**

FreeLLMAPI acts as a single-user proxy that forwards requests to multiple LLM providers. When you expose this service on a network—whether on a LAN, cloud instance, or behind a reverse proxy—you must harden it across multiple layers to prevent credential theft and unauthorized access. The `tashfeenahmed/freellmapi` repository implements several security controls that you configure through environment variables and deployment practices.

## Encrypt Provider API Keys with AES-256-GCM

FreeLLMAPI stores upstream provider API keys encrypted rather than in plain text. The encryption system uses **AES-256-GCM** with a 64-character hexadecimal `ENCRYPTION_KEY` that you must generate for production deployments.

In [`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts), the application validates the key length at startup (lines 19–26) and uses it to encrypt all credentials stored in the `api_keys` table. The encryption key is loaded from the environment and cached for the process lifetime.

Generate a production-ready key using Node.js:

```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

```

Store this value in the `ENCRYPTION_KEY` environment variable. Never commit this key to version control.

## Control Network Binding and CORS

By default, FreeLLMAPI binds to `127.0.0.1` to prevent accidental public exposure. You configure network behavior through environment variables defined in `.env.example`.

To expose the service on a trusted LAN, set `HOST_BIND=0.0.0.0`. Restrict dashboard access by whitelisting origins in `DASHBOARD_ORIGINS`:

```bash
HOST_BIND=0.0.0.0
DASHBOARD_ORIGINS="https://my-dashboard.example.com,https://admin.example.com"

```

The dashboard routes in [`server/src/routes/auth.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/auth.ts) enforce these CORS restrictions. Keep the default localhost binding unless you explicitly intend to serve external traffic.

## Authenticate Dashboard and Proxy Requests

FreeLLMAPI uses two distinct authentication layers: session-based access for the administrative dashboard and unified API keys for the proxy endpoint.

**Dashboard Authentication** ([`server/src/services/auth.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/auth.ts)) implements email/password accounts with **bcrypt** password hashing. Sessions are identified by SHA-256 hashes of 32-byte random tokens. Create an admin user via the internal API or database seeding:

```typescript
import { createUser } from './server/src/services/auth.js';

const user = createUser('admin@example.com', 'StrongPa$$w0rd');
console.log('Created user', user.id);

```

**Unified API Keys** ([`server/src/routes/proxy.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/proxy.ts)) protect the `/v1` proxy endpoint. Clients must present the key as `Authorization: Bearer <key>` or `X-API-Key`. The server validates the key against the encrypted `api_keys` table before routing to upstream providers.

Generate a unified key via the dashboard API:

```bash
curl -X POST http://localhost:3001/api/keys \
  -H "Authorization: Bearer $(cat ~/.session-token)" \
  -d '{"platform":"openai","label":"production-key"}'

```

The response returns the raw key once; store it securely on the client side.

## Enforce Rate Limiting and Cooldowns

The rate limiting service ([`server/src/services/ratelimit.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/ratelimit.ts)) implements sliding-window counters for both per-minute and per-day limits. Counters persist to SQLite, allowing limits to survive process restarts.

When a request returns 429, 402, or 403 from an upstream provider, the system triggers a cooldown stored in `rate_limit_cooldowns`. Configure the per-minute limit via `PROXY_RATE_LIMIT_RPM`:

```bash
PROXY_RATE_LIMIT_RPM=120

```

Set this to `0` to disable rate limiting entirely, though this is not recommended for production.

## Terminate TLS and Audit Logs

FreeLLMAPI serves plain HTTP and expects you to run it behind an HTTPS reverse proxy such as Nginx or Caddy. The reverse proxy terminates TLS and forwards traffic to the internal port specified by the `PORT` variable (default 3001).

For audit logging, the application redacts sensitive data in [`server/src/lib/error-redaction.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-redaction.ts). Request logs store only hashed session IDs and masked API keys, ensuring that compromised logs do not leak credentials.

## Production Deployment Checklist

Deploy FreeLLMAPI securely using Docker with the following configuration:

```bash

# Generate encryption key

export ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")

# Create environment file

cat > .env <<EOF
ENCRYPTION_KEY=$ENCRYPTION_KEY
PORT=3001
HOST_BIND=127.0.0.1
PROXY_RATE_LIMIT_RPM=120
DASHBOARD_ORIGINS="https://dashboard.example.com"
PROXY_URL="http://corporate-proxy:3128"
EOF

# Run container

docker run -d \
  -p 3001:3001 \
  -v $(pwd)/data:/app/data \
  --env-file .env \
  --name freellmapi \
  ghcr.io/tashfeenahmed/freellmapi:latest

```

When using an outbound proxy for provider traffic, configure `PROXY_URL` with an HTTP or SOCKS5 URL. The `proxyFetch` function in [`server/src/lib/proxy.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/proxy.ts) routes requests through this proxy while supporting per-platform bypass lists.

Make LLM requests using the unified key:

```bash
curl -X POST http://localhost:3001/v1/chat/completions \
  -H "Authorization: Bearer <UNIFIED_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "model":"gpt-3.5-turbo",
    "messages":[{"role":"user","content":"Hello"}]
  }'

```

If rate limits are exceeded, the server returns HTTP 429 with a `Retry-After` header.

## Summary

- **Encrypt all secrets**: Use a 64-character hex `ENCRYPTION_KEY` to enable AES-256-GCM encryption of provider API keys in the database.
- **Bind carefully**: Keep `HOST_BIND` at `127.0.0.1` unless exposing to a trusted network, and always whitelist `DASHBOARD_ORIGINS`.
- **Authenticate strictly**: Protect the dashboard with bcrypt-hashed passwords and the proxy endpoint with unified API keys validated via bearer tokens.
- **Limit abuse**: Configure `PROXY_RATE_LIMIT_RPM` to enable sliding-window rate limiting and automatic cooldowns on provider errors.
- **Proxy TLS**: Run behind Nginx or Caddy for HTTPS termination; configure `PROXY_URL` for outbound corporate proxy support.
- **Audit safely**: Rely on built-in error redaction and hashed session IDs to prevent credential leakage in logs.

## Frequently Asked Questions

### How do I generate a secure ENCRYPTION_KEY for production?

Run `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"` to generate a 64-character hexadecimal string. Set this as the `ENCRYPTION_KEY` environment variable. The server validates the length at startup in [`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts) (lines 19–26) and uses it to encrypt all stored provider credentials.

### Can I run FreeLLMAPI without a reverse proxy?

While technically possible by setting `HOST_BIND=0.0.0.0`, this is not recommended for production. The service listens on plain HTTP, so running without a reverse proxy exposes traffic to interception and manipulation. Always terminate TLS through Nginx, Caddy, or another reverse proxy when exposing the service on any network.

### How are API keys protected in the database?

Provider API keys are encrypted using AES-256-GCM before storage in the SQLite `api_keys` table. The encryption uses the `ENCRYPTION_KEY` environment variable and is handled by functions in [`server/src/lib/crypto.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts). The unified API key used for client authentication is also stored encrypted, and only the hashed session tokens appear in logs.

### What happens when rate limits are exceeded?

When a client exceeds the `PROXY_RATE_LIMIT_RPM` threshold or triggers a cooldown from upstream provider errors (429/402/403), the service in [`server/src/services/ratelimit.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/ratelimit.ts) returns HTTP 429 with a `Retry-After` header. The cooldown state persists in SQLite and memory to prevent immediate retry floods.