a month ago
Our FastAPI service has an endpoint that runs 0–3 min of work before sending its response. Every request that produces zero response bytes for ~60s is severed at the edge, while our app runs to completion and logs a clean 200.
Evidence:
Edge HTTP log: requestId=CCQ9ik6jQRS6lL2nFFmdQQ, 2026-07-15T11:18:02Z, edgeRegion=us-east4-eqdc4a, deploymentId=b4fe0096-a6f8-4b96-a2cc-f33d95de2c6d — httpStatus=499, totalDuration=60002, upstreamRqDuration=59996, txBytes=0, upstreamErrors: "client has closed the request before the server could send a response".
The client did not close: httpx with a 300s timeout gets “Server disconnected without sending a response” at ~60.8s (curl: exit 52 at ~60.4s). Reproduced from multiple networks and clients, HTTP/1.1 and HTTP/2.
A raw TLS connection to the same host that sends no bytes gets EOF from the edge at exactly ~60s; idle TCP on the same client network survives 75s+, ruling out our side.
Once the app sends any byte early (we now stream a heartbeat every 15s), the same requests complete fine past 200s — so this is a response-first-byte/header-wait timeout, not the documented 15-min request cap.
Is this true that the edge has a ~60s timeout waiting for the first upstream response byte? The docs only state the 15-minute maximum. If so, is it configurable?
4 Replies
a month ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • about 1 month ago
a month ago
The 60-second value in Railway’s documentation probably not the timeout affecting this request.
Railway documents two separate edge constraints:
- Proxy keep-alive timeout: 60 seconds
- Maximum HTTP request duration: 15 minutes
In a very similar Station thread, a request failed after 79 seconds and someone attributed it to Railway’s 60-second keep-alive value.
A Railway employee corrected that interpretation and said that an active response can run for up to 15 minutes. But can't confirm that 100%.
The heartbeat test is useful evidence, but it can also keep another timeout alive, including one in:
- the HTTP client;
- an SDK or frontend runtime;
- a CDN or reverse proxy in front of Railway;
- an ASGI middleware/proxy configuration;
- an application server timeout.
A 499 also normally indicates that the downstream/client side of the connection disappeared before the upstream response completed. The fact that FastAPI later logs 200 is compatible with the client or an intermediary disconnecting while the application continues processing.
I would test the endpoint directly, bypassing the normal client:
curl -v --http1.1 \
--max-time 180 \
https://your-domain.example/slow-endpointThen repeat against the Railway-generated *.up.railway.app domain or maybe the custom domain. This helps determine whether an additional CDN or DNS proxy is involved.
a month ago
- Where does Railway document the proxy keep-alive timeout? link?
- There is no other proxy in front of Railway and I created the client with a 900s timeout.
- Yes 499 is client closed the request but I controlled the client and it didn't. I think it's the railway proxy/edge mislabelling it. It's 60s on the dot: "httpStatus=499, totalDuration=60002, txBytes=0, upstreamErrors="client has closed the request…"
- I tried curl as well.
a month ago
The Railway documentation reference is here:
https://docs.railway.com/networking/public-networking/specs-and-limits
It lists:
- Proxy Keep-Alive timeout: 60 seconds
-
- Maximum HTTP request duration: 15 minutes
However, the documented keep-alive value should not be presented as the explanation for this behaviour. The keep-alive timeout does not control how long an active request can remain open.
The combination of:
totalDuration=60002
upstreamRqDuration=59996
txBytes=0plus successful completion when an early heartbeat is sent is strong evidence of a timeout somewhere in Railway's request path while waiting for the initial upstream response headers or bytes.
I agree that the "499" classification is questionable here. The edge could terminate the downstream connection and subsequently record the resulting state as a client-side disconnect.
The silent raw TLS test is slightly less conclusive because a TLS connection that never sends an HTTP request may legitimately be covered by an idle connection timeout. However, the completed HTTP requests using "curl" and "httpx", together with the Railway request log, are much stronger evidence.
At this point, Railway staff probably would need to inspect request ID.
a month ago
The docs say the 60s doesn't apply to HTTP/2 but I tried using HTTP/2 between the client and the Railway edge and that still doesn't work. Maybe because the Railway edge still uses HTTP/1.1 between it and my service.
Hopefully Railway staff can help clarify things.