Skip to content

Secret Offloading

MirApi provides two ways to keep upstream API credentials out of your application code and out of your logs. Both approaches inject the Authorization header into the upstream request at the proxy edge — your backend code never needs to hold plaintext secrets.

Option 1: Ephemeral Pass-Through (X-Identity-Key)

Section titled “Option 1: Ephemeral Pass-Through (X-Identity-Key)”

Pass your API key in the X-Identity-Key header. MirApi reads it into memory, injects it as the Authorization header into the upstream request, and never writes it to any log or persistent storage.

Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Identity-Key: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'

What happens:

  1. X-Identity-Key: Bearer sk_live_... is read from your request header
  2. The header is stripped from the request (never forwarded as X-Identity-Key)
  3. Authorization: Bearer sk_live_... is injected into the upstream request
  4. Stripe receives a standard Authorization header
  5. The key value is never written to any log

Your application logs may show the outbound header name but the value should be kept in your secrets manager. MirApi’s internal logs never record header values.

Terminal window
# Stripe payment
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Identity-Key: Bearer sk_live_..." \
-d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'
# OpenAI chat completion
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.openai.com/v1/chat/completions" \
-H "X-Identity-Key: Bearer sk-proj-..." \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
# Twilio (with Basic auth format)
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.twilio.com/2010-04-01/Accounts/$ACCOUNT_SID/Messages" \
-H "X-Identity-Key: Basic $TWILIO_B64_CREDENTIALS" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "To=+1234567890&From=+0987654321&Body=Hello"
# Any API that uses a custom API key header — use the header directly instead
curl https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.sendgrid.com/v3/mail/send" \
-H "Authorization: Bearer SG.xxx..." # Pass directly if the key isn't secret from logs

Option 2: Encrypted Database Storage (X-Proxy-Master-Key)

Section titled “Option 2: Encrypted Database Storage (X-Proxy-Master-Key)”

Store your upstream credentials encrypted in the MirApi database. At request time, provide the decryption passphrase in X-Proxy-Master-Key. The gateway decrypts the credential and injects it as Authorization — your passphrase and the plaintext credential are never stored together.

This approach is more secure for production environments because:

  • Your application only stores the master passphrase (not the upstream API keys)
  • Rotating upstream credentials is done in the MirApi dashboard — no code deploys needed
  • Multiple credentials can be managed centrally
  1. Open your MirApi dashboard → Credentials → Add Credential
  2. Enter:
    • Name: A label (e.g., stripe-production)
    • Target host pattern: api.stripe.com (or a substring match)
    • Value: Your API key (sk_live_...)
    • Encryption passphrase: Your master key
  3. The dashboard encrypts the value using AES-GCM and stores only the ciphertext
Terminal window
# Gateway matches credential by target host ("api.stripe.com" matches the stored pattern)
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Proxy-Master-Key: my-master-passphrase" \
-H "Content-Type: application/json" \
-d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'

The gateway:

  1. Reads X-Proxy-Master-Key from the request
  2. Looks up credentials in the database that match the target hostname
  3. Decrypts the stored ciphertext using the provided passphrase
  4. Injects Authorization: Bearer sk_live_... into the upstream request
  5. The passphrase and plaintext are discarded from memory after the request

If you have multiple credentials matching the same hostname, specify which one to use:

Terminal window
# Production credential
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Proxy-Master-Key: my-master-passphrase" \
-H "X-Credential-ID: 4a2e5d18-df99-4d66-a212-3cb5d9f0f9b3" \
-d '{"amount": 2000}'
# Staging credential (different UUID, same passphrase)
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Proxy-Master-Key: my-master-passphrase" \
-H "X-Credential-ID: 9b7c3f21-aa44-4e9d-bc01-1234567890ab" \
-d '{"amount": 100}'

Rotating a compromised key requires only a dashboard update — no code change or deployment:

  1. In the dashboard, find the credential
  2. Update the value with the new API key (re-encrypted with the same passphrase)
  3. All future requests immediately use the new key

X-Identity-KeyX-Proxy-Master-Key
StoragePassed per-request, never storedStored encrypted in DB
SetupNone — just include the headerRequires dashboard setup
Key rotationChange value in your app/secrets managerChange only in MirApi dashboard
Best forDevelopment, simple setups, or keys already in your secrets managerProduction multi-service environments