Webhook 408 Request Timeout — Causes and Fixes
A practical guide to diagnosing and eliminating webhook timeouts — covering slow handlers, async processing patterns, and how to prevent retry storms.
Quick Answer
A 408 means your server accepted the connection but didn't respond in time. Most webhook providers set a 5–30 second timeout window. Fix it by responding immediately with 200 OK and processing the payload asynchronously.
How Webhook Timeouts Work
When a webhook provider delivers an event, it opens an HTTP connection to your endpoint and waits for a response. If your server doesn't reply within the timeout window, the provider closes the connection and marks the delivery as failed.
Most providers then retry the delivery — sometimes immediately, sometimes with exponential backoff. If your handler is consistently slow, you can end up with a growing backlog of retries, all of which timeout, creating a retry storm.
Different providers have very different timeout windows:
| Provider | Timeout Window | Retry Policy |
|---|---|---|
| Stripe | 30 seconds | Up to 3 days with exponential backoff |
| GitHub | 10 seconds | 3 attempts over 2 hours |
| Shopify | 5 seconds | 19 retries over 48 hours |
| Discord | 3 seconds | Disables webhook after repeated failures |
Discord's 3-second window is particularly aggressive. Any handler that does real work within the request-response cycle will struggle to meet it consistently.
Why Webhook Handlers Time Out
Timeouts happen when a handler does too much work synchronously before returning a response. Common causes:
Database writes and queries
Running complex queries, multiple inserts, or waiting for a slow connection pool inside the request path adds unpredictable latency.
Calling external APIs
Making HTTP requests to third-party services (sending email, posting to Slack, calling a payment API) blocks the handler until those calls complete or timeout themselves.
File operations and generation
PDF generation, image processing, or writing large files to disk can take seconds and should never block the response.
Synchronous business logic
Running inventory calculations, sending confirmation emails, or updating multiple dependent records all add to the total response time.
Cold starts and under-provisioned infrastructure
Serverless functions spinning up cold, or an underpowered server under load, can push any handler past the provider's timeout threshold.
The Fix: Respond Fast, Process Async
The correct pattern for any webhook handler that does real work is: acknowledge immediately, process in the background. Return a 200 OK as soon as you've validated the request and put the payload somewhere safe to process later.
// Node.js / Express — respond immediately, process async
app.post('/webhook', (req, res) => {
// Respond immediately — before doing any real work
res.status(200).json({ received: true });
// Don't await this — fire and forget into background processing
processAsync(req.body);
});
async function processAsync(payload) {
// Safe to do slow work here — the HTTP response already went out
await db.orders.create({ data: payload });
await sendConfirmationEmail(payload.customer.email);
await updateInventory(payload.items);
}For higher-volume or more reliable processing, push the payload onto a message queue (Redis, SQS, BullMQ, etc.) and process it in a separate worker process:
// Pattern with a job queue (BullMQ example)
import { Queue } from 'bullmq';
const webhookQueue = new Queue('webhooks');
app.post('/webhook', async (req, res) => {
// Validate signature first (fast — just crypto)
validateSignature(req);
// Enqueue the payload — takes < 1ms
await webhookQueue.add('process', req.body);
// Respond immediately
res.status(200).json({ received: true });
});
// Separate worker process handles the heavy work
worker.process('process', async (job) => {
await processOrderCreated(job.data);
});The queue approach also gives you retry logic, dead-letter queues for failed jobs, and observability into processing backlogs — all outside the HTTP request cycle.
How to Debug Timeouts with Requex
Requex can help in two ways when debugging timeout issues:
1. Inspect what the provider sends
Point the provider to a Requex URL to capture the exact payload. Knowing the payload size and structure helps you estimate how long your handler will take to process it — large payloads with nested objects may trigger slow parsing or complex queries.
2. Simulate delayed and timeout responses
Requex lets you configure your webhook URL to respond after a configurable delay. Use this to test how your sending system behaves when the endpoint is slow — do retries stack up? Does the sender backoff correctly? Does your deduplication logic handle duplicate deliveries?
Set the response delay to match your provider's timeout window (e.g., 6 seconds for Shopify's 5-second limit) and trigger test events to observe the retry behavior.
Fix Checklist
- ✓Return 200 OK immediately after validating the signature — before doing any database or network operations.
- ✓Move all heavy work (DB writes, email sending, external API calls) to a background job or queue.
- ✓Check your database connection pool — a saturated pool causes all handlers to wait for a connection.
- ✓If using serverless, add provisioned concurrency or keep-warm pings to reduce cold start latency.
- ✓Add idempotency checks in your background worker so retried deliveries don't create duplicate records.
- ✓Monitor your p99 handler latency — even a handler averaging 2 seconds will timeout on Shopify's 5-second window under load.
Related Resources
Webhook Retry Failed
Understand retry schedules and how to handle repeated deliveries
Webhook 500 Error
Fix server errors and crashing handlers that also trigger retries
Debug Webhook Errors
General troubleshooting guide for all webhook error types
Webhook Simulator
Simulate slow and timeout responses to test retry behavior
Test How Your Handler Behaves Under Timeout Conditions
Use Requex to capture incoming payloads and simulate delayed responses — so you can observe retry behavior before it happens in production.
Open Requex →