---
title: "Match timeouts from proxy to app and stop · Agent Wranglers"
url: https://agentwranglers.se/playbook/operations/capacity-and-cost/timeout-chain
id: OPS-70
level: Recommended
updated: 2026-10-22
tokens: 1122
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Match timeouts from proxy to app and stop abandoned work

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Capacity and cost](https://agentwranglers.se/playbook/operations/capacity-and-cost.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-22.

- ID: OPS-70
- Level: Recommended
- Chapter: [Capacity and cost](https://agentwranglers.se/playbook/operations/capacity-and-cost.md)
- Rule: Give each layer a timeout a few seconds shorter than the layer in front of it. That runs from the load balancer to the proxy, the app and the database. Stop work as soon as the client or the proxy gives up on it.
- Check: A test lists the timeout at each layer and fails unless each is shorter than the one in front. On staging, start a request that runs past the app's timeout, then close the client. The app's log shows the work cancelled within 1 second, and a small page fetched meanwhile keeps its normal time.
- How: When the proxy gives up first, it answers 504 while the app keeps working on a request nobody will read. Under load those orphaned requests fill every worker, and the site stays down after the visitors leave. Set the app's request timeout about 5 seconds below the proxy's read timeout. In nginx, `proxy_read_timeout` defaults to 60 seconds and counts the time between two reads, not the whole answer. Pass a cancel signal from the request into every database query and outgoing call, and cancel it when the connection closes. Run work that needs longer in a separate worker pool or queue, as [Move slow work out of the request](https://agentwranglers.se/playbook/scaling/background-jobs-and-queues/slow-work-off-requests.md) says. Each call's own limit comes from [Set timeouts on every call](https://agentwranglers.se/playbook/api-design/reliability/timeouts.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### nginx example

```nginx
location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_read_timeout 30s;
}
```

### JavaScript example

```js
const PROXY_READ_TIMEOUT_MS = 30_000;
const APP_TIMEOUT_MS = PROXY_READ_TIMEOUT_MS - 5_000;

app.get('/reports/:id', async (req, res) => {
  const abandoned = new AbortController();
  res.on('close', () => { if (!res.writableEnded) abandoned.abort(); });
  const signal = AbortSignal.any([abandoned.signal, AbortSignal.timeout(APP_TIMEOUT_MS)]);
  res.json(await buildReport(req.params.id, { signal }));
});
```

### Python example

```python
import asyncio

from fastapi import HTTPException, Request

PROXY_READ_TIMEOUT_SECONDS = 30
APP_TIMEOUT_SECONDS = PROXY_READ_TIMEOUT_SECONDS - 5
POLL_SECONDS = 0.5


@app.get("/reports/{report_id}")
async def report(report_id: str, request: Request):
    work = asyncio.create_task(build_report(report_id))
    deadline = asyncio.get_running_loop().time() + APP_TIMEOUT_SECONDS
    while not work.done():
        if await request.is_disconnected() or asyncio.get_running_loop().time() > deadline:
            work.cancel()
            raise HTTPException(status_code=503)
        await asyncio.wait({work}, timeout=POLL_SECONDS)
    return work.result()
```

## Sources

- [nginx proxy module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html)

## Related rules

- SCL-16 [Move slow work out of the request](https://agentwranglers.se/playbook/scaling/background-jobs-and-queues/slow-work-off-requests.md) in Scaling
- API-35 [Set timeouts on every call](https://agentwranglers.se/playbook/api-design/reliability/timeouts.md) in API design

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/operations/capacity-and-cost/timeout-chain">Agent Wranglers operations and observability playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Turn work away before overload](https://agentwranglers.se/playbook/operations/capacity-and-cost/load-shedding.md)
- Next: [Cap retries across the whole service](https://agentwranglers.se/playbook/operations/capacity-and-cost/retry-budget.md)
- [Capacity and cost](https://agentwranglers.se/playbook/operations/capacity-and-cost.md)
