{"record":{"id":"985f4fb59d6638e0","repo":"snail007/goproxy","slug":"dead-loop-detected-s","errorCode":null,"errorMessage":"dead loop detected , %s","messagePattern":"dead loop detected , (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/http.go","lineNumber":115,"sourceCode":"\tlog.Printf(\"use proxy : %v, %s\", useProxy, address)\n\t//os.Exit(0)\n\terr = s.OutToTCP(useProxy, address, &inConn, &req)\n\tif err != nil {\n\t\tif *s.cfg.Parent == \"\" {\n\t\t\tlog.Printf(\"connect to %s fail, ERR:%s\", address, err)\n\t\t} else {\n\t\t\tlog.Printf(\"connect to %s parent %s fail\", *s.cfg.ParentType, *s.cfg.Parent)\n\t\t}\n\t\tutils.CloseConn(&inConn)\n\t}\n}\nfunc (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err error) {\n\tinAddr := (*inConn).RemoteAddr().String()\n\tinLocalAddr := (*inConn).LocalAddr().String()\n\t//防止死循环\n\tif s.IsDeadLoop(inLocalAddr, req.Host) {\n\t\tutils.CloseConn(inConn)\n\t\terr = fmt.Errorf(\"dead loop detected , %s\", req.Host)\n\t\treturn\n\t}\n\tvar outConn net.Conn\n\tvar _outConn interface{}\n\tif useProxy {\n\t\t_outConn, err = s.outPool.Pool.Get()\n\t\tif err == nil {\n\t\t\toutConn = _outConn.(net.Conn)\n\t\t}\n\t} else {\n\t\toutConn, err = utils.ConnectHost(address, *s.cfg.Timeout)\n\t}\n\tif err != nil {\n\t\tlog.Printf(\"connect to %s , err:%s\", *s.cfg.Parent, err)\n\t\tutils.CloseConn(inConn)\n\t\treturn\n\t}\n","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/snail007/goproxy/blob/e6d6a821db80e7f47ee6e981a144984e1d4ddb3d/services/http.go#L97-L133","documentation":"OutToTCP forwards an incoming HTTP connection to a TCP parent (optionally through a proxy). Before connecting it calls IsDeadLoop(inLocalAddr, req.Host): if the local address of the outbound path equals the host being requested, the service would connect back to itself and loop forever, so it closes the incoming connection and returns this error. It is a self-protection mechanism against recursive proxying.","triggerScenarios":"The configured parent/proxy address resolves to the same host:port the service itself is listening on, so a request to req.Host would be forwarded back into this very service; IsDeadLoop detects the match and OutToTCP aborts the forwarding.","commonSituations":"Parent configured as localhost/127.0.0.1 with the same port as this proxy; DNS or /etc/hosts entry making the parent resolve back to the service itself; chained proxies configured in a cycle (A->B->A); port-forwarding rule looping traffic back.","solutions":["Fix the parent/proxy config so it points to a genuinely different upstream host:port, not this service's own address","Trace the proxy chain and break any cycle (A->B->A) by pointing one hop at the real destination","Check DNS/hosts entries that cause the parent hostname to resolve to the local machine","If a loop is intentional for testing, bind the service and the parent to clearly distinct addresses/ports"],"exampleFix":"// before (nps.conf points parent at itself -> loop)\n// http_proxy_ip=127.0.0.1\n// http_proxy_port=8024  (same port this service listens on)\n// after: point parent at the real upstream\n// http_proxy_ip=upstream.example.com\n// http_proxy_port=8080","handlingStrategy":"validation","validationCode":"func isSelfReference(parentHost string, parentPort, listenPort int) bool {\n    addrs, _ := net.LookupHost(parentHost)\n    locals, _ := net.InterfaceAddrs() // or simply compare to 127.0.0.1/hostname\n    for _, a := range addrs {\n        for _, l := range locals {\n            if ipnet, ok := l.(*net.IPNet); ok && ipnet.IP.String() == a && parentPort == listenPort {\n                return true\n            }\n        }\n    }\n    return false\n}\n// refuse to start if parent resolves to the service's own listen address","typeGuard":null,"tryCatchPattern":"if err := svc.Run(); err != nil {\n    if strings.Contains(err.Error(), \"dead loop detected\") {\n        log.Fatalf(\"parent %q points back at this service; fix parent/proxy config\", cfg.Parent)\n    }\n}","preventionTips":["Never point parent/proxy at the same host:port the service listens on","Diagram the proxy chain before deploying multi-hop setups and check for cycles","Pin parent addresses to IPs to avoid DNS resolving back to self","Add a config lint step that resolves the parent and compares it to the listen address"],"tags":["proxy","loop","tcp","configuration"],"backgroundTag":"proxy-dead-loop-detected","analyzedSha":"e6d6a821db80e7f47ee6e981a144984e1d4ddb3d","analyzedAt":"2026-09-03T15:32:42.750Z","contentChangedAt":"2026-09-03T15:32:42.750Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}