Proxy Failover and High Availability Strategies
Production systems cannot tolerate proxy failures. When a proxy connection times out or returns errors, your application must failover instantly to an alternative path. This guide covers failover patterns from simple retry logic to multi-tier redundancy architectures.
Why Proxy Failover Matters
Even the most reliable proxy infrastructure experiences occasional issues: - Individual proxy IPs may be temporarily blocked by a target - Network routes can experience brief congestion - Target sites may rate-limit specific IP ranges
A failover strategy converts these transient issues into invisible blips rather than application errors.
Strategy 1: Retry with Rotation
The simplest failover — retry failed requests with a different proxy session:
import httpx
import time@dataclass(frozen=True) class FailoverResult: url: str status: int body: str attempts: int final_proxy: str
def fetch_with_failover( url: str, username: str, password: str, max_retries: int = 3, ) -> FailoverResult: for attempt in range(max_retries): session_id = f"failover-{attempt}-{int(time.time())}" proxy_url = f"http://{username}-session-{session_id}:{password}@gate.hexproxies.com:8080" try: with httpx.Client(proxy=proxy_url, timeout=15) as client: resp = client.get(url) if resp.status_code == 200: return FailoverResult( url=url, status=200, body=resp.text, attempts=attempt + 1, final_proxy=session_id, ) if resp.status_code == 403: continue # IP blocked, try next session except httpx.TimeoutException: continue # Timeout, try next session except httpx.ConnectError: time.sleep(1) continue
return FailoverResult(url=url, status=0, body="", attempts=max_retries, final_proxy="none") ```
Strategy 2: Multi-Tier Proxy Fallback
Cascade through proxy types — try ISP first (fastest), fall back to residential (most diverse):
@dataclass(frozen=True) class ProxyTier: name: str proxy_url: str timeout: int
def build_tier_chain(username: str, password: str) -> list[ProxyTier]: return [ ProxyTier( name="isp", proxy_url=f"http://{username}:{password}@isp.hexproxies.com:8080", timeout=10, ), ProxyTier( name="residential", proxy_url=f"http://{username}:{password}@gate.hexproxies.com:8080", timeout=20, ), ]
def fetch_with_tier_fallback(url: str, tiers: list[ProxyTier]) -> dict: for tier in tiers: try: with httpx.Client(proxy=tier.proxy_url, timeout=tier.timeout) as client: resp = client.get(url) if resp.status_code == 200: return {"tier": tier.name, "status": 200, "body": resp.text} except Exception: continue return {"tier": "none", "status": 0, "body": ""} ```
Strategy 3: Health-Checked Proxy Pool
Maintain a pool of proxy sessions and continuously health-check them:
import asyncio
import time@dataclass(frozen=True) class ProxyHealth: session_id: str last_check: float is_healthy: bool avg_latency_ms: float
class HealthCheckedPool: def __init__(self, username: str, password: str, pool_size: int = 10): self._username = username self._password = password self._pool: list[ProxyHealth] = [ ProxyHealth( session_id=f"pool-{i}", last_check=0, is_healthy=True, avg_latency_ms=0, ) for i in range(pool_size) ]
def get_healthy_proxy(self) -> str: healthy = [p for p in self._pool if p.is_healthy] if not healthy: healthy = self._pool # Fall back to all if none marked healthy best = min(healthy, key=lambda p: p.avg_latency_ms) return f"http://{self._username}-session-{best.session_id}:{self._password}@gate.hexproxies.com:8080"
def update_health(self, session_id: str, is_healthy: bool, latency_ms: float) -> None: self._pool = [ ProxyHealth( session_id=p.session_id, last_check=time.monotonic(), is_healthy=is_healthy if p.session_id == session_id else p.is_healthy, avg_latency_ms=latency_ms if p.session_id == session_id else p.avg_latency_ms, ) for p in self._pool ] ```
Strategy 4: Circuit Breaker Pattern
Stop sending traffic to failing proxy paths and auto-recover:
class ProxyCircuitBreaker: def __init__(self, failure_threshold: int = 5, recovery_time: float = 60): self._failure_count = 0 self._failure_threshold = failure_threshold self._recovery_time = recovery_time self._last_failure: float = 0 self._state = "closed" # closed=normal, open=blocking, half_open=testing
@property def is_available(self) -> bool: if self._state == "closed": return True if self._state == "open": if time.monotonic() - self._last_failure > self._recovery_time: self._state = "half_open" return True return False return True # half_open
def record_success(self) -> None: self._failure_count = 0 self._state = "closed"
def record_failure(self) -> None: self._failure_count += 1 self._last_failure = time.monotonic() if self._failure_count >= self._failure_threshold: self._state = "open" ```
Monitoring Failover Events
Track failover frequency, tier usage, and recovery times. High failover rates may indicate that your target site has updated its defenses or that you need to adjust your request patterns.
Hex Proxies infrastructure delivers 99.99% uptime across our ISP and residential networks. Our 100G transit and 400Gbps edge capacity ensure that failover events are rare — but when they occur, your application should handle them gracefully.