Skip to content

Fix Twilio signature validation behind reverse proxy#5541

Merged
mdmohsin7 merged 2 commits intomainfrom
fix/twilio-signature-validation
Mar 10, 2026
Merged

Fix Twilio signature validation behind reverse proxy#5541
mdmohsin7 merged 2 commits intomainfrom
fix/twilio-signature-validation

Conversation

@mdmohsin7
Copy link
Member

Summary

  • Twilio webhook /v1/phone/twiml was returning 403 "Invalid Twilio signature" on every call
  • Root cause: request.url returns the internal pod/proxy URL (e.g. http://pod-ip:8080/...), but Twilio signs requests using the public URL configured in the TwiML App (https://api.omi.me/v1/phone/twiml)
  • Fix: reconstruct the canonical URL from BASE_API_URL env var (already available) so the signature matches

Test plan

  • Deploy to dev and verify phone calls connect (no more 403 on TwiML webhook)
  • Verify existing non-phone-call endpoints are unaffected

🤖 Generated with Claude Code

Use BASE_API_URL to reconstruct the canonical public URL for signature
validation instead of request.url, which reflects the internal proxy URL.
Twilio signs requests using the TwiML App's configured URL (e.g.
https://api.omi.me/v1/phone/twiml), but request.url returns the pod's
internal address, causing every webhook to fail with 403.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR fixes a 403 Invalid Twilio Signature error on the /v1/phone/twiml TwiML webhook when the backend is running behind a reverse proxy. The root cause is that Twilio signs its webhook requests using the public-facing URL (e.g. https://api.omi.me/v1/phone/twiml), but request.url inside the pod returns the internal URL (e.g. http://pod-ip:8080/v1/phone/twiml), causing every signature check to fail. The fix reads the already-available BASE_API_URL environment variable to reconstruct the canonical public URL for signature validation, consistent with how other routers in this codebase use that variable.

Key changes:

  • import os added (previously missing from this file)
  • URL for Twilio signature validation is now derived from BASE_API_URL env var when set, falling back to str(request.url) for local development
  • The path /v1/phone/twiml is hardcoded as a string literal (see inline comment — using request.url.path would be more resilient to future route renames)

Confidence Score: 4/5

  • This PR is safe to merge — it correctly fixes a real production bug with minimal scope and no behavioural regressions.
  • The change is small (5 lines), well-understood, and consistent with how BASE_API_URL is used throughout the rest of the codebase. The fallback to str(request.url) preserves existing local-dev behaviour. The only concern is a minor maintainability issue with the hardcoded path literal rather than a logic error.
  • No files require special attention beyond the inline comment on the hardcoded path in backend/routers/phone_calls.py.

Important Files Changed

Filename Overview
backend/routers/phone_calls.py Correctly fixes Twilio signature validation behind a reverse proxy by reconstructing the canonical public URL from BASE_API_URL env var; minor maintainability concern with the hardcoded /v1/phone/twiml path literal that duplicates the route decorator.

Sequence Diagram

sequenceDiagram
    participant Client as Twilio Client SDK
    participant Twilio as Twilio Cloud
    participant Proxy as Reverse Proxy
    participant App as FastAPI App (pod)

    Client->>Twilio: Initiate call
    Twilio->>Proxy: POST /v1/phone/twiml\n(signed with public URL https://api.omi.me/v1/phone/twiml)
    Proxy->>App: Forward POST\n(request.url = http://pod-ip:8080/v1/phone/twiml)

    Note over App: Before fix: url = str(request.url)\n→ http://pod-ip:8080/v1/phone/twiml\n→ signature mismatch → 403

    Note over App: After fix: url = BASE_API_URL + /v1/phone/twiml\n→ https://api.omi.me/v1/phone/twiml\n→ matches Twilio-signed URL → 200

    App->>App: validate_twilio_signature(url, params, signature)
    App->>Twilio: TwiML response (VoiceResponse)
    Twilio->>Client: Call connected
Loading

Last reviewed commit: 6ab40b4

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@mdmohsin7 mdmohsin7 merged commit a713d59 into main Mar 10, 2026
1 check passed
@mdmohsin7 mdmohsin7 deleted the fix/twilio-signature-validation branch March 10, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant