fix(healthcheck): probe domain nodes by node domain, not resolved ip#13743
Merged
AlinsRan merged 1 commit intoJul 24, 2026
Merged
Conversation
AlinsRan
force-pushed
the
fix/healthcheck-node-domain-host
branch
from
July 24, 2026 03:27
95d3fd7 to
010c740
Compare
…lved ip An active health check has no downstream client -- the gateway itself is the client probing the node, so it must address the node by the node's own identity (its configured domain). pass_host only decides how a *client* request's Host is forwarded, which is undefined here (there is no client Host). compute_targets gated the probe Host header on pass_host == "node", so a domain-based upstream with the default pass_host got a nil Host header. The health-check library then falls back to the resolved ip, so the probe sends Host: <ip> (hitting the wrong virtual host) and, over HTTPS, SNI: <ip> (the TLS handshake fails), marking otherwise-healthy domain upstreams as unhealthy. Always carry node.domain as the probe Host header (nil for ip nodes, so they are unaffected), except when pass_host is "rewrite", which still pins upstream_host. This makes the probe honor the documented default of checks.active.host being the node host. Tests cover both the HTTP Host header and the HTTPS SNI of the probe.
AlinsRan
force-pushed
the
fix/healthcheck-node-domain-host
branch
from
July 24, 2026 03:43
010c740 to
550f96b
Compare
shreemaan-abhishek
approved these changes
Jul 24, 2026
nic-6443
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
With the default
pass_host, an active health check on a domain-based HTTPS upstream always marks the node unhealthy, so the whole upstream is taken out of service and never recovers. The active probe sends the TLS SNI as the resolved ip, so the handshake fails against a backend whose certificate is issued for the domain. The same root cause affects plain HTTP: the probe sendsHost: <resolved-ip>, hitting the wrong virtual host and getting a misleading result.Root cause
An active health check runs independently of any request — there is no downstream client. The gateway itself is the client probing the node, so the probe must address the node by the node's own identity: its configured domain.
pass_hostis a request-forwarding rule (how a client request's Host is passed to the upstream); it has no meaning for a client-less probe.compute_targets()inapisix/healthcheck_manager.luanonetheless derives the probe Host header by reusing that request-path logic:For a domain node with the default
pass_host(pass) this yields anilHost header, and the health-check library then falls back tohostname, which is the resolved ip for a domain node. Both the probeHostheader and the HTTPSSNIderive from this value, so both become the ip.Real traffic is unaffected: for
pass_host = node/rewritethe request Host/SNI isnode.domain/upstream_host, and forpassthe real client's Host is forwarded. Only the client-less health checker degrades to the ip.Fix
Always carry
node.domainas the probe Host header, except whenpass_hostisrewrite(which still pinsupstream_hostviaup_hdr). The probe then addresses the node by its own identity, independent ofpass_host.node.domainisnilfor ip-based nodes, so ip upstreams are unaffected.Per
pass_hostmode:pass_hostnodenode.domainnode.domain(unchanged)rewriteupstream_hostupstream_host(unchanged)pass(default), domain nodenil→ probe uses resolved ipnode.domainnilnil(unchanged)Tests
t/node/healthcheck-node-domain-host.t, both with a domain node (test.com, resolved locally to127.0.0.1) and the defaultpass_host:httpsupstream +type: httpsactive check; the TLS probe target logs$ssl_server_name. Asserts the probe's SNI istest.com, not127.0.0.1.Hostheader. Asserts the probe sendsHost: test.com, notHost: 127.0.0.1.Checklist