What's Actually Happening
Cloudflare returns a 524 timeout error when the origin server takes too long to respond. The request exceeds Cloudflare's 100-second timeout limit.
The Error You'll See
Error 524: A timeout occurred
A timeout occurred while waiting for your web server to respond.Browser error:
<h1>524 timeout</h1>
<p>A timeout occurred</p>Cloudflare dashboard:
Status: 524
Error: Timeout OccurredCurl test:
```bash $ curl -I https://example.com
HTTP/2 524 ```
Why This Happens
- 1.Long-running requests - Origin takes > 100 seconds to respond
- 2.Heavy database queries - Slow database operations
- 3.Large file processing - Server processing large files
- 4.External API calls - Waiting for slow external services
- 5.Server overload - Origin server under heavy load
- 6.Network issues - Connectivity problems between Cloudflare and origin
Step 1: Check Error Details
```bash # Check Cloudflare logs: # Cloudflare Dashboard -> Analytics & Logs -> Logs
# Note the URL path causing timeout # Note the time of occurrence
# Test direct to origin: curl -I http://origin-server-ip/path
# Test through Cloudflare: curl -I https://example.com/path
# Check response time: time curl -I https://example.com/path
# Check with verbose output: curl -v https://example.com/path 2>&1 | grep -E "timeout|time_total" ```
Step 2: Bypass Cloudflare for Testing
```bash # Test origin directly:
# Get origin IP: dig example.com +short
# Or check DNS: nslookup example.com
# Test with hosts file: # Add to /etc/hosts: # ORIGIN_IP example.com
# Test: curl -I http://origin-ip/path
# Or test with Host header: curl -H "Host: example.com" http://origin-ip/path
# Compare response times: # Direct vs through Cloudflare
# If direct is fast, issue might be Cloudflare-to-origin connectivity ```
Step 3: Optimize Long-Running Processes
```php // PHP: Process in background
// Don't wait for completion: ignore_user_abort(true); set_time_limit(0);
// Process in background: exec('php /path/to/script.php > /dev/null 2>&1 &');
// Return immediately: header('Content-Type: application/json'); echo json_encode(['status' => 'processing']); ```
```python # Python: Use async processing
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def long_running_task(): # Heavy processing time.sleep(120)
@app.post("/process") async def process(background_tasks: BackgroundTasks): background_tasks.add_task(long_running_task) return {"status": "processing"} ```
```javascript // Node.js: Use job queue
const Queue = require('bull'); const processQueue = new Queue('processing');
app.post('/process', async (req, res) => { await processQueue.add({ data: req.body }); res.json({ status: 'queued' }); }); ```
Step 4: Increase Server Resources
```bash # Check server resources:
# CPU: top -bn1 | head -20
# Memory: free -m
# Disk I/O: iostat -x 1 5
# Network: netstat -i
# Check slow queries: # MySQL: mysql -e "SHOW FULL PROCESSLIST"
# PostgreSQL: psql -c "SELECT * FROM pg_stat_activity WHERE state = 'active'"
# Optimize database: # - Add indexes # - Optimize queries # - Increase cache
# Increase PHP limits: # php.ini: max_execution_time = 300 memory_limit = 512M ```
Step 5: Implement Polling Pattern
```javascript // Client-side polling:
async function processLargeFile(fileId) { // Start processing const response = await fetch('/api/process', { method: 'POST', body: JSON.stringify({ fileId }) });
const { taskId } = await response.json();
// Poll for status
let status = 'pending';
while (status === 'pending') {
await new Promise(r => setTimeout(r, 5000)); // Wait 5 seconds
const statusResponse = await fetch(/api/status/${taskId});
status = (await statusResponse.json()).status;
}
return status; } ```
```python # Server-side task status:
import uuid from flask import Flask, jsonify
tasks = {}
@app.route('/api/process', methods=['POST']) def start_process(): task_id = str(uuid.uuid4()) tasks[task_id] = {'status': 'pending'} # Start background task process_in_background.delay(task_id) return jsonify({'taskId': task_id})
@app.route('/api/status/<task_id>') def get_status(task_id): return jsonify(tasks.get(task_id, {})) ```
Step 6: Use Webhooks for Completion
```python # Start long process with webhook callback:
@app.route('/api/process', methods=['POST']) def process_with_webhook(): webhook_url = request.json.get('webhookUrl')
def long_task(): result = do_heavy_processing() # Call webhook when done requests.post(webhook_url, json=result)
threading.Thread(target=long_task).start() return jsonify({'status': 'processing'}) ```
```javascript // Client receives webhook:
app.post('/webhook/complete', (req, res) => { const result = req.body; // Handle completion notifyUser(result); res.sendStatus(200); }); ```
Step 7: Configure Cloudflare Timeouts
```bash # Cloudflare timeout options:
# 1. Enterprise plan: Extended timeout # Contact Cloudflare support for 600-second timeout
# 2. Use Cloudflare Workers: # Workers can run longer (30s CPU, 15min wall time)
// worker.js addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })
async function handleRequest(request) { // Forward to origin with longer wait const response = await fetch(originUrl, { // Worker can wait longer }) return response }
# 3. Use Argo Smart Routing: # Reduces latency between Cloudflare and origin # Cloudflare Dashboard -> Traffic -> Argo
# 4. Adjust origin response timeout: # Not directly configurable on free plans ```
Step 8: Check Origin Server Logs
```bash # Check web server logs:
# Nginx: tail -f /var/log/nginx/error.log grep -i timeout /var/log/nginx/error.log
# Apache: tail -f /var/log/apache2/error.log grep -i timeout /var/log/apache2/error.log
# Check access logs for slow requests: awk '$NF > 60' /var/log/nginx/access.log # Requests > 60 seconds
# PHP-FPM: tail -f /var/log/php-fpm/error.log
# Check PHP slow log: tail -f /var/log/php-fpm/slow.log
# Application logs: tail -f /var/www/html/storage/logs/laravel.log # Laravel tail -f /var/www/html/wp-content/debug.log # WordPress ```
Step 9: Implement Caching
```nginx # Cache heavy responses:
# Nginx caching: proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
server { location /api/heavy-endpoint { proxy_cache my_cache; proxy_cache_valid 200 10m; proxy_pass http://origin; } }
# Cache at application level:
# Redis caching: $result = $redis->get('cache_key'); if (!$result) { $result = heavy_operation(); $redis->setex('cache_key', 600, $result); } return $result; ```
```php // PHP Laravel cache:
$result = Cache::remember('heavy_data', 600, function () { return DB::select('SELECT /* heavy query */'); }); ```
Step 10: Cloudflare 524 Verification Script
```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-cf-524.sh #!/bin/bash
DOMAIN=$1 PATH=${2:-"/"}
echo "=== DNS Resolution ===" dig $DOMAIN +short
echo "" echo "=== Direct Origin Test ===" ORIGIN_IP=$(dig $DOMAIN +short | head -1) echo "Origin IP: $ORIGIN_IP" echo "Testing direct connection..." time curl -I -m 120 --resolve $DOMAIN:80:$ORIGIN_IP http://$DOMAIN$PATH 2>&1 | head -10
echo "" echo "=== Cloudflare Test ===" echo "Testing through Cloudflare..." time curl -I -m 120 https://$DOMAIN$PATH 2>&1 | head -10
echo "" echo "=== Cloudflare Status ===" curl -s https://www.cloudflarestatus.com/api/v2/summary.json | jq '.status.description'
echo "" echo "=== Check Response Codes ===" echo "Direct: $(curl -s -o /dev/null -w '%{http_code}' --resolve $DOMAIN:80:$ORIGIN_IP http://$DOMAIN$PATH)" echo "Cloudflare: $(curl -s -o /dev/null -w '%{http_code}' https://$DOMAIN$PATH)"
echo "" echo "=== Recommendations ===" echo "1. If origin slow: optimize server performance" echo "2. If origin fast but CF slow: check Cloudflare connectivity" echo "3. For long processes: implement async/background jobs" echo "4. Consider Cloudflare Workers for extended timeout" echo "5. Use polling pattern for requests > 100 seconds" EOF
chmod +x /usr/local/bin/check-cf-524.sh
# Usage: /usr/local/bin/check-cf-524.sh example.com /api/endpoint ```
Cloudflare 524 Checklist
| Check | Command | Expected |
|---|---|---|
| Direct origin | curl origin | Response < 100s |
| Through CF | curl https://domain | Response < 100s |
| Server resources | top, free | Available |
| Database queries | SHOW PROCESSLIST | No slow queries |
| Error logs | grep timeout | No timeout errors |
| Process design | code review | Async for long ops |
Verify the Fix
```bash # After fixing 524 timeout
# 1. Test endpoint directly curl -I http://origin-ip/path // Response < 100 seconds
# 2. Test through Cloudflare curl -I https://example.com/path // HTTP 200, no 524
# 3. Check response time time curl https://example.com/path // Under 100 seconds
# 4. For long processes curl -X POST https://example.com/api/process // Returns task ID immediately
# 5. Monitor Cloudflare logs # Dashboard -> Analytics // No 524 errors
# 6. Load test ab -n 100 -c 10 https://example.com/path // All requests succeed ```
Related Issues
- [Fix Cloudflare 522 Connection Timed Out](/articles/fix-cloudflare-522-connection-timed-out)
- [Fix Cloudflare 521 Web Server Down](/articles/fix-cloudflare-521-web-server-down)
- [Fix Cloudflare 523 Origin Unreachable](/articles/fix-cloudflare-523-origin-unreachable)