{"record":{"id":"d9c184a78cfc98a6","repo":"decolua/9router","slug":"proxyfetch-proxy-failed-falling-back-to-direct","errorCode":null,"errorMessage":"[ProxyFetch] Proxy failed, falling back to direct bypass: ${proxyError.message}","messagePattern":"\\[ProxyFetch\\] Proxy failed, falling back to direct bypass: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"open-sse/utils/proxyFetch.js","lineNumber":324,"sourceCode":"    return originalFetch(vercelRelayUrl, { ...options, headers: relayHeaders });\n  }\n\n  const connectionProxyUrl = resolveConnectionProxyUrl(targetUrl, proxyOptions);\n  const envProxyUrl = connectionProxyUrl ? null : normalizeProxyUrl(getEnvProxyUrl(targetUrl));\n  const proxyUrl = connectionProxyUrl || envProxyUrl;\n\n  // MITM DNS bypass: for known MITM-intercepted hosts, resolve real IP to avoid DNS spoof\n  if (shouldBypassMitmDns(targetUrl)) {\n    if (proxyUrl) {\n      // Proxy resolves DNS externally (not affected by /etc/hosts) — use proxy directly\n      try {\n        const dispatcher = await getDispatcher(proxyUrl);\n        return await originalFetch(url, { ...options, dispatcher });\n      } catch (proxyError) {\n        if (proxyOptions?.strictProxy === true) {\n          throw new Error(`[ProxyFetch] Proxy required but failed (strictProxy=true): ${proxyError.message}`);\n        }\n        console.warn(`[ProxyFetch] Proxy failed, falling back to direct bypass: ${proxyError.message}`);\n      }\n    }\n    // No proxy — manually resolve real IP to bypass DNS spoof\n    try {\n      const parsedUrl = new URL(targetUrl);\n      const realIP = await resolveRealIP(parsedUrl.hostname);\n      if (realIP) return await createBypassRequest(parsedUrl, realIP, options);\n    } catch (error) {\n      console.warn(`[ProxyFetch] MITM bypass failed: ${error.message}`);\n    }\n  }\n\n  if (proxyUrl) {\n    try {\n      const dispatcher = await getDispatcher(proxyUrl);\n      return await originalFetch(url, { ...options, dispatcher });\n    } catch (proxyError) {\n      // If strictProxy is enabled, fail hard instead of falling back to direct","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/utils/proxyFetch.js#L306-L342","documentation":"When a proxy is configured for a request, proxyAwareFetch attempts the fetch through the proxy dispatcher (undici ProxyAgent). If the proxied attempt throws and strictProxy is not enabled, it logs this warning and retries directly, resolving the real IP via DNS to bypass potential DNS spoofing. Only when strictProxy=true does the proxy failure propagate as a thrown error.","triggerScenarios":"The proxied fetch threw — proxy host unreachable/wrong port, proxy auth rejected (407), proxy timeout, TLS failure through the proxy, or getDispatcher failed to build an agent for the proxy URL.","commonSituations":"Proxy server down or the wrong address/port in proxy config; corporate proxy requires credentials not supplied; SOCKS vs HTTP proxy type mismatch; firewall blocks the proxy port; proxy certificate issues.","solutions":["Verify the proxy URL (protocol, host, port, credentials) in proxy settings and test it with curl","If traffic MUST go through the proxy, set strictProxy=true so failures throw instead of leaking direct requests","Check that the proxy is running and reachable from the gateway host","Confirm proxy auth credentials are included in the proxy URL (http://user:pass@host:port)","If direct fallback is acceptable, treat this warning as informational and fix the proxy at leisure"],"exampleFix":"// before: silent direct fallback on proxy failure\nproxyOptions: { proxyUrl: \"http://127.0.0.1:9999\" }\n// after: hard-fail when the proxy is required\nproxyOptions: { proxyUrl: \"http://127.0.0.1:9999\", strictProxy: true }","handlingStrategy":"try-catch","validationCode":"const u = new URL(proxyUrl);\nif (!/^https?:$/.test(u.protocol)) throw new Error('proxy must be http(s)');\nawait fetch('http://' + u.host + '/', { signal: AbortSignal.timeout(3000) }); // proxy reachable?","typeGuard":"const isProxyFailure = (e) => e instanceof Error && /Proxy required but failed|proxy/i.test(e.message);","tryCatchPattern":"try {\n  return await proxyAwareFetch(url, { proxyOptions });\n} catch (e) {\n  if (isProxyFailure(e)) {\n    // strictProxy threw — proxy is mandatory in this environment\n    console.error('proxy mandatory but down:', e.message);\n  }\n  throw e;\n}","preventionTips":["Health-check the proxy at startup and periodically","Include credentials in the proxy URL if the proxy requires auth (407)","Match the proxy URL scheme to its actual type (http vs socks5)","Set strictProxy=true in environments where direct egress is forbidden, so failures are loud","Monitor this warning — frequent direct bypass may silently leak traffic past the proxy"],"tags":["proxy","network","fallback","dns-bypass"],"backgroundTag":"proxy-connection-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}