12 hours ago
Hello Railway Support,
I need help with a production platform-routing issue affecting my Railway service.
Service: Tony-OS
Project ID: 8d27897b-924a-4da2-84de-4021374a1c8a
Service ID: 131a855c-cd6e-403e-9ed4-fb3c7ab1cb4e
Custom domain: app.tonyosagency.com
Problem:
Resend is sending a real POST webhook to:
https://app.tonyosagency.com/api/inbound/reply
Resend successfully generates the email.received event and attempts webhook delivery, but Railway returns HTTP 404 before the request reaches my Flask application.
Evidence:
- Resend receiving is verified and working.
- Resend generates real email.received events.
- Resend webhook configuration is enabled and points to the correct URL.
- DNS/MX is verified.
- Railway custom domain is verified and the certificate is valid.
- Direct requests to the exact same URL reach my Flask application and correctly return HTTP 401 when unauthenticated.
- Railway HTTP-edge logs show Resend-origin POST requests returning 404.
- The Flask application has no corresponding request trace for those Resend-origin requests, indicating the 404 occurs before Flask.
- I performed a clean Railway service restart.
- The restart did not change the behavior.
- After the restart, Resend again generated a real email.received event and again received HTTP 404 from the Railway edge.
- The service is otherwise healthy, running one replica with no crashes.
- The persistent volume and PostgreSQL data remained intact after the restart.
Representative Railway HTTP-edge results:
POST /api/inbound/reply → 404
This occurs specifically for Resend webhook delivery.
A direct request to the identical URL from another origin reaches the application and returns 401 as expected.
Could you please investigate why Railway's edge/routing layer is returning 404 for POST requests from Resend's webhook-delivery infrastructure while the same URL is reachable from other origins?
This appears to be a Railway edge/routing issue rather than an application-code, DNS, or Resend configuration issue.
I have deliberately not made further application, DNS, database, or Resend changes because the evidence points to the Railway edge.
Please let me know what additional request IDs, logs, or diagnostics you need.
Thank you.
1 Replies
12 hours ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • about 12 hours ago
an hour ago
One specific setting worth inspecting is Edge Rules, if any are enabled for Tony-OS. Railway's current documentation says a rule can match a client IP or header and return a custom 400–499 status, including 404, before forwarding to the service. A rule could therefore explain different results by sender; I haven't seen your configuration and am not saying one exists.
Also, a 404 appearing in HTTP logs plus no Flask trace does not yet locate the failure. Flask's request lifecycle includes WSGI middleware outside Flask. I tested Flask 3.1.3: the same POST URL returned 401 normally, but a fixture middleware returned 404 without running even before_request.
For the next natural failing delivery, compare one 404 and one direct POST control using:
@host:app.tonyosagency.com AND @path:/api/inbound/reply AND @method:POSTKeep their UTC times, requestId, clientUa, srcIp, edgeRegion, and response headers/body with secrets removed. These are documented HTTP-log filters; Resend also publishes its delivery IPs.
If your current trace is inside Flask, a temporary outer WSGI logger can locate the boundary. Wrap the actual WSGI callable your server loads, outside existing middleware:
import json
def trace_wsgi(wrapped):
def traced(environ, start_response):
fields = {
"requestId": environ.get("HTTP_X_RAILWAY_REQUEST_ID"),
"method": environ.get("REQUEST_METHOD"),
"path": environ.get("PATH_INFO"),
"host": environ.get("HTTP_HOST"),
"forwardedHost": environ.get("HTTP_X_FORWARDED_HOST"),
}
def log(event, **extra):
print(json.dumps({"message": "wsgi_ingress", "event": event,
**fields, **extra}), flush=True)
log("enter")
def traced_start(status, headers, exc_info=None):
log("response_headers", status=status)
return start_response(status, headers, exc_info)
return wrapped(environ, traced_start)
return traced
# Standard Flask setup: install last, after existing app.wsgi_app wrappers.
app.wsgi_app = trace_wsgi(app.wsgi_app)This leaves request bodies and signature checks untouched. If the failing Railway request ID appears with 404 here, the wrapped application stack produced that status. If it never appears while the control does, the gap is before this boundary or in logging; Railway support can then investigate the specific request IDs and configured edge rules. That absence alone still doesn't prove a platform defect.
Chandan