The "REALTIME_TIMEOUT: Connection to Realtime server timed out" error occurs when Supabase Realtime cannot establish or maintain a WebSocket connection to the realtime server. This typically happens due to network issues, firewall restrictions, browser extensions, or server-side configuration problems. The error prevents realtime features like live subscriptions, presence, and broadcast channels from working.
The "REALTIME_TIMEOUT: Connection to Realtime server timed out" error is a Supabase Realtime error that indicates the client cannot establish a WebSocket connection to Supabase's realtime server within the expected timeframe. Supabase Realtime is built on PostgreSQL's logical replication and WebSockets, allowing real-time updates from your database. When you subscribe to changes (using `supabase.channel()` and `.on()`), the client attempts to connect to `wss://<project-ref>.supabase.co/realtime/v1` via WebSocket. This timeout error typically occurs when: 1. **Network connectivity issues** - Firewalls, proxies, or network restrictions blocking WebSocket connections 2. **Browser/extension interference** - Ad blockers, privacy extensions, or browser settings preventing WebSocket connections 3. **Server-side configuration** - CORS issues, incorrect project configuration, or regional server problems 4. **Client-side issues** - Incorrect initialization, missing authentication, or library version mismatches 5. **Infrastructure limits** - Free tier limitations or rate limiting on realtime connections The error is particularly common in development environments, corporate networks with strict security policies, or when using certain browser extensions that interfere with WebSocket connections.
First, eliminate common browser and network issues:
1. Disable browser extensions:
- Temporarily disable ad blockers (uBlock Origin, AdBlock Plus)
- Disable privacy extensions (Privacy Badger, Ghostery)
- Turn off security extensions that might block WebSockets
- Test in incognito/private mode (extensions are usually disabled)
2. Check network connectivity:
# Test WebSocket connectivity using wscat
npm install -g wscat
wscat -c "wss://YOUR_PROJECT_REF.supabase.co/realtime/v1"
# Alternative using curl for WebSocket handshake test
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: YOUR_PROJECT_REF.supabase.co" -H "Origin: https://YOUR_PROJECT_REF.supabase.co" https://YOUR_PROJECT_REF.supabase.co/realtime/v13. Test from different networks:
- Try from home network vs corporate network
- Test with mobile hotspot
- Check if VPN is interfering
4. Verify WebSocket support in browser:
// Check if WebSocket is supported
if (window.WebSocket) {
console.log('WebSocket supported');
} else {
console.error('WebSocket not supported - use a modern browser');
}
// Test basic WebSocket connection
const testSocket = new WebSocket('wss://echo.websocket.org');
testSocket.onopen = () => console.log('Test WebSocket connected');
testSocket.onerror = (e) => console.error('Test WebSocket error:', e);Ensure your Supabase project is properly configured for Realtime:
1. Check project URL and keys:
// Correct initialization
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = 'https://YOUR_PROJECT_REF.supabase.co'
const supabaseKey = 'YOUR_ANON_KEY' // Not service role key for client-side
const supabase = createClient(supabaseUrl, supabaseKey, {
realtime: {
params: {
eventsPerSecond: 10 // Adjust based on needs
}
}
})2. Enable Realtime for your tables:
- Go to Supabase Dashboard → Database → Replication
- Click "Enable Realtime" for tables you want to subscribe to
- Or use SQL:
-- Enable Realtime for a specific table
alter publication supabase_realtime add table your_table_name;
-- Enable Realtime for all tables (not recommended for production)
alter publication supabase_realtime add table all_tables;
-- Check which tables have Realtime enabled
select * from pg_publication_tables where pubname = 'supabase_realtime';3. Configure CORS settings:
- Go to Supabase Dashboard → Settings → API
- Add your application domains to "Site URL" and "Additional Redirect URLs"
- For development: add http://localhost:3000, http://localhost:5173, etc.
- For production: add your production domain
- Save changes and wait a few minutes for propagation
4. Check project region:
- Go to Supabase Dashboard → Settings → General
- Note your project region
- Ensure it matches where most users are located
- Consider migrating if region is causing latency issues
Adjust network settings to allow WebSocket connections:
1. Allow WebSocket ports:
- WebSocket over TLS (wss) uses port 443 (same as HTTPS)
- Ensure outbound traffic to *.supabase.co:443 is allowed
- For corporate firewalls, whitelist:
- *.supabase.co
- Ports: 443 (HTTPS/wss)
2. Configure proxy settings if needed:
// If behind a corporate proxy, you may need to configure it
// Note: Most browsers use system proxy settings automatically
// For Node.js environments, you might need:
const { createClient } = require('@supabase/supabase-js');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyAgent = new HttpsProxyAgent('http://proxy.company.com:8080');
const supabase = createClient(supabaseUrl, supabaseKey, {
global: {
fetch: (url, options) => {
return fetch(url, {
...options,
agent: proxyAgent
});
}
}
});3. Test network connectivity:
# Test DNS resolution
nslookup YOUR_PROJECT_REF.supabase.co
# Test HTTPS connectivity
curl -I https://YOUR_PROJECT_REF.supabase.co
# Test WebSocket endpoint specifically
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" -H "Sec-WebSocket-Version: 13" https://YOUR_PROJECT_REF.supabase.co/realtime/v14. Check for SSL/TLS issues:
- Ensure system clock is correct (SSL certificates depend on accurate time)
- Update root certificates if needed
- Try disabling SSL verification temporarily for testing (not for production):
// WARNING: Only for testing in development
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';Add robust error handling and retry logic:
1. Implement exponential backoff for reconnections:
class RealtimeConnection {
constructor(supabase, channelName, options = {}) {
this.supabase = supabase;
this.channelName = channelName;
this.maxRetries = options.maxRetries || 5;
this.retryCount = 0;
this.channel = null;
}
async connect() {
try {
this.channel = this.supabase.channel(this.channelName);
// Subscribe to events
this.channel
.on('broadcast', { event: 'test' }, (payload) => {
console.log('Received:', payload);
})
.subscribe((status) => {
if (status === 'SUBSCRIBED') {
console.log('Connected to Realtime');
this.retryCount = 0; // Reset on success
} else if (status === 'CHANNEL_ERROR') {
this.handleError();
} else if (status === 'TIMED_OUT') {
this.handleTimeout();
}
});
} catch (error) {
this.handleError(error);
}
}
handleError(error) {
console.error('Realtime error:', error);
if (this.retryCount < this.maxRetries) {
const delay = Math.pow(2, this.retryCount) * 1000; // 1s, 2s, 4s, 8s, 16s
this.retryCount++;
console.log(`Retrying in ${delay}ms (attempt ${this.retryCount}/${this.maxRetries})`);
setTimeout(() => {
this.connect();
}, delay);
} else {
console.error('Max retries reached. Giving up.');
// Implement fallback logic or notify user
}
}
handleTimeout() {
console.log('Realtime connection timed out');
this.handleError(new Error('Connection timeout'));
}
}2. Add connection state monitoring:
// Monitor connection state
const channel = supabase.channel('room1');
channel.on('system', { event: 'disconnect' }, () => {
console.log('Disconnected from Realtime');
// Attempt reconnection
});
channel.on('system', { event: 'reconnect' }, () => {
console.log('Reconnected to Realtime');
});
channel.subscribe((status) => {
console.log('Channel status:', status);
switch(status) {
case 'SUBSCRIBED':
console.log('Successfully subscribed');
break;
case 'CHANNEL_ERROR':
console.error('Channel error occurred');
break;
case 'TIMED_OUT':
console.error('Connection timed out');
break;
case 'CLOSED':
console.log('Channel closed');
break;
}
});3. Implement heartbeat/ping mechanism:
// Send periodic pings to keep connection alive
let heartbeatInterval;
channel.subscribe((status) => {
if (status === 'SUBSCRIBED') {
// Start heartbeat
heartbeatInterval = setInterval(() => {
channel.send({
type: 'broadcast',
event: 'heartbeat',
payload: { timestamp: Date.now() }
});
}, 30000); // Every 30 seconds
} else if (status === 'CLOSED' || status === 'CHANNEL_ERROR') {
// Clear heartbeat
clearInterval(heartbeatInterval);
}
});If you're on Free tier, consider these optimizations or upgrades:
1. Free tier limitations:
- 2 concurrent realtime connections per project
- 500 MB database space
- Limited to 1-day log retention
Check if you're hitting limits:
-- Check active Realtime connections
SELECT count(*) as active_connections
FROM pg_stat_activity
WHERE application_name LIKE '%supabase_realtime%';2. Optimize connection usage:
// Share channels instead of creating new ones
const sharedChannel = supabase.channel('shared-channel');
// Multiple components can listen to the same channel
sharedChannel.on('postgres_changes', {
event: 'INSERT',
schema: 'public',
table: 'messages'
}, (payload) => {
// Update all components listening for new messages
});
// Instead of:
// const channel1 = supabase.channel('comp1');
// const channel2 = supabase.channel('comp2');
// const channel3 = supabase.channel('comp3');3. Implement connection pooling on client side:
class ConnectionPool {
constructor() {
this.connections = new Map();
}
getChannel(name) {
if (!this.connections.has(name)) {
const channel = supabase.channel(name);
this.connections.set(name, channel);
}
return this.connections.get(name);
}
cleanup() {
this.connections.forEach((channel, name) => {
if (channel.subscription) {
supabase.removeChannel(channel);
}
});
this.connections.clear();
}
}4. Consider upgrading to Pro tier:
- $25/month: Unlimited realtime connections
- 8 GB database + 100 GB bandwidth
- 7-day log retention
- Better support and reliability
Upgrade in Supabase Dashboard → Billing
Use Supabase's debugging tools and community resources:
1. Enable debug logging:
// Enable verbose logging
const supabase = createClient(supabaseUrl, supabaseKey, {
auth: {
persistSession: true,
autoRefreshToken: true,
},
realtime: {
params: {
eventsPerSecond: 10,
},
logLevel: 'debug' // Enable debug logging
}
});2. Check Supabase status and community:
- Visit [status.supabase.com](https://status.supabase.com) for platform status
- Check [github.com/supabase/realtime](https://github.com/supabase/realtime) for issues
- Search [supabase.com/docs](https://supabase.com/docs) for Realtime documentation
- Join [supabase.com/discord](https://supabase.com/discord) for community support
3. Test with different clients:
# Test with simple WebSocket client
# Install: npm install -g wscat
wscat -c "wss://YOUR_PROJECT_REF.supabase.co/realtime/v1" -H "Authorization: Bearer YOUR_ANON_KEY"
# Send a test message
{"topic":"realtime:public:messages","event":"phx_join","payload":{},"ref":"1"}4. Create minimal reproduction:
<!-- Minimal test HTML file -->
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
</head>
<body>
<script>
const supabaseUrl = 'https://YOUR_PROJECT_REF.supabase.co';
const supabaseKey = 'YOUR_ANON_KEY';
const supabase = window.supabase.createClient(supabaseUrl, supabaseKey);
const channel = supabase.channel('test-channel');
channel.subscribe((status) => {
console.log('Status:', status);
document.body.innerHTML += `<p>Status: ${status}</p>`;
});
setTimeout(() => {
supabase.removeChannel(channel);
}, 10000);
</script>
</body>
</html>5. Contact Supabase support:
- For Pro/Enterprise: Use dashboard support
- Include: error logs, reproduction steps, network traces
- Specify: browser/OS version, network environment, error frequency
## Understanding Supabase Realtime Architecture
Supabase Realtime uses a combination of technologies:
1. PostgreSQL Logical Replication: Captures database changes
2. Phoenix/Elixir Channels: Manages WebSocket connections
3. PostgreSQL Publication/Subscription: Controls which tables emit changes
### Connection Flow:
1. Client → WebSocket handshake → Realtime Server (Phoenix)
2. Realtime Server → PostgreSQL LISTEN/NOTIFY for presence/broadcast
3. PostgreSQL Logical Replication → Realtime Server → Client
### Timeout Configuration
Default timeouts in @supabase/realtime-js:
- Connection timeout: 30 seconds
- Heartbeat interval: 30 seconds
- Reconnect attempts: 5 with exponential backoff
### Common Pitfalls
1. CORS Preflight Issues:
- WebSocket doesn't use CORS preflight, but initial HTTP handshake might
- Ensure OPTIONS requests are allowed to realtime endpoint
2. WebSocket Subprotocols:
- Supabase uses Phoenix protocol over WebSocket
- Some proxies may strip or modify WebSocket headers
3. SSL/TLS Inspection:
- Corporate SSL inspection can break WebSocket connections
- Try adding certificate to trusted store
4. IPv6 vs IPv4:
- Some networks have IPv6 issues
- Supabase supports both, but network configuration matters
### Performance Considerations
1. Connection Pooling:
- Each browser tab creates separate WebSocket connection
- Consider shared workers for multiple tabs
2. Message Size:
- Large payloads can cause timeouts
- Use eventsPerSecond to limit frequency
3. Subscription Management:
- Unsubscribe from channels when not needed
- Use supabase.removeChannel() to clean up
### Monitoring and Metrics
Key metrics to monitor:
- WebSocket connection success rate
- Message delivery latency
- Reconnection frequency
- Error rate by error type
### Fallback Strategies
When Realtime fails:
1. Polling fallback: Switch to periodic fetch requests
2. Local storage: Cache data locally when disconnected
3. Queue system: Queue updates for when connection restores
4. Offline-first: Design app to work without realtime
### Security Considerations
1. Authentication:
- Use RLS policies for database access control
- Validate JWT tokens on server for sensitive operations
2. Rate Limiting:
- Implement client-side rate limiting
- Use Supabase's built-in rate limiting
3. Data Validation:
- Validate all realtime messages on server
- Sanitize broadcast payloads
email_conflict_identity_not_deletable: Cannot delete identity because of email conflict
How to fix "Cannot delete identity because of email conflict" in Supabase
mfa_challenge_expired: MFA challenge has expired
How to fix "mfa_challenge_expired: MFA challenge has expired" in Supabase
conflict: Database conflict, usually related to concurrent requests
How to fix "database conflict usually related to concurrent requests" in Supabase
phone_exists: Phone number already exists
How to fix "phone_exists" in Supabase
StorageApiError: resource_already_exists
StorageApiError: Resource already exists