error http

HTTP 503 Service Unavailable

Understanding HTTP 503 Service Unavailable - the server is temporarily unable to handle the request due to overload or maintenance.

What It Means

HTTP 503 Service Unavailable indicates that the server is currently unable to handle the request due to temporary overloading or scheduled maintenance. This is a temporary condition — the server should include a Retry-After header to suggest when the client should try again.

Common Causes

  • Server is under heavy load and cannot accept new requests
  • Scheduled maintenance or planned downtime
  • Application is deploying or restarting
  • Database connection pool is exhausted
  • Dependent service is down
  • Auto-scaling has not yet provisioned enough capacity
  • Health check failures causing load balancer to mark instances as unhealthy

How to Fix

Server-side: Implement maintenance mode

// Express.js maintenance mode
const maintenanceMode = process.env.MAINTENANCE === 'true';

app.use((req, res, next) => {
  if (maintenanceMode) {
    return res.status(503)
      .set('Retry-After', '3600') // Try again in 1 hour
      .json({
        error: 'Service Unavailable',
        message: 'The server is under maintenance. Please try again later.'
      });
  }
  next();
});

Nginx maintenance page

# Show maintenance page when a file exists
server {
    if (-f /var/www/maintenance.flag) {
        return 503;
    }

    error_page 503 @maintenance;
    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
        add_header Retry-After 3600;
    }
}

Client-side: Handle 503 with retry

async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 503) {
      const retryAfter = response.headers.get('Retry-After');
      const delay = retryAfter
        ? parseInt(retryAfter) * 1000
        : Math.pow(2, attempt) * 1000;

      console.log(`Service unavailable. Retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }

    return response;
  }
  throw new Error('Service remained unavailable after retries');
}

Monitor and fix overload

# Check server load
uptime
top -bn1 | head -5

# Check database connections
# PostgreSQL
psql -c "SELECT count(*) FROM pg_stat_activity;"

# MySQL
mysql -e "SHOW STATUS LIKE 'Threads_connected';"

# Check if workers are maxed out
# PM2
pm2 monit

# Gunicorn
ps aux | grep gunicorn | wc -l

Health check endpoint

app.get('/health', async (req, res) => {
  try {
    await db.query('SELECT 1');
    res.json({ status: 'healthy', timestamp: Date.now() });
  } catch (error) {
    res.status(503).json({
      status: 'unhealthy',
      error: 'Database connection failed'
    });
  }
});
  • HTTP 500 - Internal Server Error: An application bug rather than a capacity issue.
  • HTTP 502 - Bad Gateway: The upstream server returned an invalid response.
  • HTTP 504 - Gateway Timeout: The upstream server timed out.
  • HTTP 429 - Too Many Requests: You specifically are rate-limited, rather than the whole service being down.