HTTP 504 Gateway Timeout

The server acting as a gateway or proxy did not receive a timely response from the upstream server. Your request reached the proxy layer, but the backend server took too long to respond and the proxy gave up waiting.

What Is HTTP 504 Gateway Timeout?

HTTP 504 Gateway Timeout is a server error status code indicating that a server acting as a gateway or proxy did not receive a timely response from an upstream server. The proxy successfully connected to the backend but the backend did not send a complete response within the proxy's configured timeout window. This is different from 502 Bad Gateway, where the proxy could not establish a connection at all or received an invalid response.

In modern web infrastructure, 504 errors typically occur when the backend application is processing a request that takes longer than the reverse proxy or load balancer allows. Common scenarios include slow database queries, unresponsive external API calls, compute-intensive operations like report generation, and memory-constrained processes that are swapping to disk. The proxy has a configured timeout, often 30 to 60 seconds for web requests, and returns 504 when this timer expires.

The solution to 504 errors depends on whether the slow response is expected or problematic. If the backend legitimately needs more time for certain operations, the architectural solution is to use asynchronous processing. The server returns 202 Accepted immediately and processes the request in the background, providing a polling endpoint or webhook for the client to retrieve the result. If the slow response is unexpected, the solution is to optimize the backend by fixing slow queries, adding caches, or setting timeouts on external API calls.

Common Causes

Slow Database Query

The backend is executing a complex or unoptimized database query that takes longer than the proxy timeout. Large table scans, missing indexes, and N+1 query patterns are common culprits.

External API Timeout

The backend is calling a third-party API that is slow or unresponsive. Without proper timeout settings, the backend waits indefinitely while the proxy timer expires.

Resource-Intensive Processing

The request triggers heavy computation such as report generation, image processing, or data transformation that exceeds the proxy timeout window.

Proxy Timeout Too Short

The reverse proxy or load balancer timeout is set too low for the expected response time of the backend. Long-running but legitimate operations are cut short by the proxy timer.

How to Fix

Optimize Slow Queries

Use database EXPLAIN plans to identify slow queries. Add appropriate indexes, optimize query structure, and avoid N+1 patterns. Consider caching frequently accessed data to eliminate repeated queries.

Set Timeout on External Calls

Add explicit timeouts to all external HTTP calls in your backend code. If a third-party API is slow, use circuit breaker patterns to fail fast rather than holding the request open.

Increase Proxy Timeout

If the backend legitimately needs more time, increase the proxy_read_timeout in Nginx or the equivalent setting in your load balancer. Be cautious, as very long timeouts tie up proxy connections.

Move to Async Processing

For long-running operations, return 202 Accepted immediately and process the request asynchronously. Provide a polling endpoint or webhook for clients to check the result when it is ready.

Code Examples

Express.js

// Express.js — timeout handling and async processing pattern
const express = require('express');
const app = express();

// Set server-level timeout
app.use((req, res, next) => {
  // Set a 30-second timeout for all routes
  req.setTimeout(30000, () => {
    if (!res.headersSent) {
      res.status(504).json({
        error: 'Gateway Timeout',
        message: 'Request took too long to process'
      });
    }
  });
  next();
});

// Async processing for long-running tasks
app.post('/api/reports', async (req, res) => {
  const jobId = generateJobId();

  // Start processing in the background
  processReportAsync(jobId, req.body).catch(err => {
    console.error(`Report job ${jobId} failed:`, err);
  });

  // Return 202 immediately — do not wait for completion
  res.status(202).json({
    jobId,
    status: 'processing',
    statusUrl: `/api/reports/${jobId}/status`
  });
});

// Poll for job completion
app.get('/api/reports/:jobId/status', async (req, res) => {
  const job = await getJob(req.params.jobId);
  if (!job) return res.status(404).json({ error: 'Job not found' });

  if (job.status === 'completed') {
    return res.json({
      status: 'completed',
      downloadUrl: job.resultUrl
    });
  }
  res.json({ status: job.status, progress: job.progress });
});

Flask (Python)

# Flask — timeout handling and async processing pattern
from flask import Flask, jsonify, request
import uuid
import threading

app = Flask(__name__)
jobs = {}

# Async processing for long-running tasks
@app.route('/api/reports', methods=['POST'])
def create_report():
    job_id = str(uuid.uuid4())
    jobs[job_id] = {'status': 'processing', 'progress': 0}

    # Start processing in a background thread
    thread = threading.Thread(
        target=process_report,
        args=(job_id, request.get_json())
    )
    thread.start()

    # Return 202 immediately
    return jsonify(
        job_id=job_id,
        status='processing',
        status_url=f'/api/reports/{job_id}/status'
    ), 202

def process_report(job_id, data):
    try:
        # ... long-running report generation
        result_url = generate_report(data)
        jobs[job_id] = {'status': 'completed', 'result_url': result_url}
    except Exception as e:
        jobs[job_id] = {'status': 'failed', 'error': str(e)}

# Poll for job completion
@app.route('/api/reports/<job_id>/status', methods=['GET'])
def report_status(job_id):
    job = jobs.get(job_id)
    if not job:
        return jsonify(error='Job not found'), 404
    return jsonify(**job)

Frequently Asked Questions

What is the difference between 504 and 502?

HTTP 504 means the proxy connected to the upstream server but it did not respond before the timeout expired. HTTP 502 means the proxy could not connect to the upstream or received an invalid response. 504 is a timeout issue; 502 is a connection or response issue.

Can I fix a 504 by increasing the timeout?

Increasing the proxy timeout may resolve the 504 but is usually the wrong fix. It keeps connections open longer, consuming resources. Fix the root cause by optimizing slow queries, adding caches, or moving long-running tasks to async processing.

What causes 504 errors on serverless platforms?

Serverless platforms like AWS Lambda, Vercel, and Cloudflare Workers have execution time limits. If your function exceeds this limit, the platform's gateway returns 504. Optimize your function or move long-running work to a different architecture.

Should I retry after a 504 error?

Yes, but with caution. If the request is idempotent (GET, PUT, DELETE), retry with exponential backoff. If the request is not idempotent (POST), retry only if you can guarantee the operation was not partially completed on the server.

How do I prevent 504 errors from slow database queries?

Add indexes to frequently queried columns, use EXPLAIN to identify full table scans, implement query result caching, use connection pooling, and set query timeout limits in your database driver configuration.

Monitor Your APIs & Services

Get instant alerts when your endpoints go down. 60-second checks, free forever.

Start Monitoring Free →