504 Gateway Timeout

Server Error - Upstream server didn't respond in time

HTTP 504 Gateway Timeout

What It Means

The HTTP 504 Gateway Timeout error occurs when a server acting as a gateway or proxy did not receive a timely response from the upstream server. The upstream is alive but too slow.

Request Flow

User → Nginx (proxy) → App Server ⏱️ (too slow) → 504

504 vs 408 vs 502

Code Who timed out
408 Client was too slow sending request
502 Upstream returned invalid response
504 Upstream took too long to respond

Common Causes

  • Slow database queries: Long-running queries blocking responses
  • Heavy computation: Request triggers expensive processing
  • External API slow: Third-party service taking too long
  • Resource contention: Server under heavy load
  • Network issues: Slow connection to upstream
  • Deadlock: Application stuck waiting

Timeout Configuration

Nginx

location / {
    proxy_pass http://app;

    # Connection timeout (establishing connection)
    proxy_connect_timeout 60s;

    # Time to receive response
    proxy_read_timeout 60s;

    # Time to send request
    proxy_send_timeout 60s;
}

AWS ALB / CloudFront

# ALB: idle_timeout.timeout_seconds (default 60)
# CloudFront: Origin response timeout (default 30s, max 60s)

How to Debug

# Check app response time directly
time curl http://localhost:3000/slow-endpoint

# Monitor slow queries
EXPLAIN ANALYZE SELECT ...;

# Check for resource exhaustion
top
iostat
netstat -an | grep ESTABLISHED | wc -l

# Application profiling
console.time('operation');
await heavyOperation();
console.timeEnd('operation');

Solutions

  • Increase timeout: Short-term fix if requests legitimately take long
  • Optimize queries: Add indexes, rewrite slow queries
  • Add caching: Cache expensive computations or API calls
  • Background jobs: Move slow work to async queues
  • Pagination: Return smaller result sets
  • Scale up: Add more servers or resources

Long-Running Request Pattern

// Instead of waiting for slow operation
// Return immediately and poll for result

app.post('/api/reports', async (req, res) => {
  const jobId = await queue.add('generateReport', req.body);
  res.status(202).json({
    status: 'processing',
    jobId,
    checkUrl: `/api/reports/status/${jobId}`
  });
});

app.get('/api/reports/status/:id', async (req, res) => {
  const job = await queue.getJob(req.params.id);
  if (job.isCompleted()) {
    res.json({ status: 'complete', result: job.result });
  } else {
    res.json({ status: 'processing' });
  }
});

Catch timeouts before users complain

UptimeSignal alerts you instantly when your endpoints start timing out.

Start monitoring free →

Related Status Codes