nautechsystems/nautilus_trader · error
{description} endpoint must use HTTPS unless its host is a c
Error message
{description} endpoint must use HTTPS unless its host is a canonical loopback IP literal What it means
Thrown by `validate_execution_endpoint` as the security gate: cleartext `http://` is only allowed when the host is a canonical loopback IP literal (127.0.0.1, [::1], etc.). Any non-loopback host must use HTTPS to protect RPC traffic (including credentials) in transit.
Source
Thrown at crates/adapters/blockchain/src/rpc/http.rs:930
pub(crate) fn validate_execution_endpoint(
endpoint: &str,
description: &str,
) -> anyhow::Result<Url> {
let url =
Url::parse(endpoint).map_err(|_| anyhow::anyhow!("Invalid {description} endpoint"))?;
anyhow::ensure!(
matches!(url.scheme(), "http" | "https"),
"{description} endpoint must use HTTPS or canonical loopback HTTP"
);
anyhow::ensure!(
url.host().is_some(),
"{description} endpoint host is required"
);
anyhow::ensure!(
url.fragment().is_none(),
"{description} endpoint fragments are unsupported"
);
anyhow::ensure!(
url.scheme() == "https" || is_canonical_loopback_endpoint(endpoint),
"{description} endpoint must use HTTPS unless its host is a canonical loopback IP literal"
);
Ok(url)
}
fn is_canonical_loopback_endpoint(endpoint: &str) -> bool {
let Some((scheme, rest)) = endpoint.split_once("://") else {
return false;
};
if !scheme.eq_ignore_ascii_case("http") && !scheme.eq_ignore_ascii_case("https") {
return false;
}
let authority_end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
let authority = &rest[..authority_end];
if authority.is_empty() || authority.contains('@') {
return false;View on GitHub (pinned to 18893faf8b)
Solutions
- Switch the endpoint to `https://` (enable TLS on the node or put it behind a TLS-terminating proxy).
- For local development, use the literal loopback form `http://127.0.0.1:8545` (or `http://[::1]:8545`), not `localhost` or a LAN IP.
- If tests need cleartext, target a loopback address so the canonical-loopback check passes.
Example fix
// before let endpoint = "http://192.168.1.10:8545"; // LAN IP over HTTP rejected // after let endpoint = "https://192.168.1.10:8545"; // or http://127.0.0.1:8545 for local dev
Defensive patterns
Strategy: validation
Validate before calling
let url = url::Url::parse(endpoint)?;
let loopback = matches!(url.host_str(), Some(h) if h == "127.0.0.1" || h == "[::1]");
anyhow::ensure!(url.scheme() == "https" || loopback,
"non-loopback endpoint '{endpoint}' must use https"); Type guard
fn is_tls_safe_endpoint(s: &str) -> bool {
url::Url::parse(s).map(|u| {
u.scheme() == "https"
|| matches!(u.host_str(), Some("127.0.0.1") | Some("[::1]"))
}).unwrap_or(false)
} Try / catch
match HttpRpcClient::new(endpoint) {
Err(e) if e.to_string().contains("canonical loopback IP literal") => {
return Err(anyhow::anyhow!("refusing cleartext HTTP to non-loopback host '{endpoint}'; enable TLS"));
}
other => other,
} Prevention
- Default all remote endpoints to https:// in every environment (dev/staging/prod).
- Reserve plain http:// for literal 127.0.0.1 / [::1] in local development only.
- Never promote local http://LAN-IP configs to shared/staging environments.
- Terminate TLS with a proxy (nginx/Caddy) in front of internal nodes.
When it happens
Trigger: Configuring an execution endpoint as `http://` against a non-loopback host — e.g. `http://node.internal:8545`, `http://192.168.1.10:8545`, or a public `http://` provider URL — via `new` or `normalize_endpoint`.
Common situations: Local dev config carried into staging/production where the node is remote; internal-network nodes assumed safe enough for plaintext; providers still advertising plain-HTTP endpoints.
Related errors
- Router {router} is not in the configured `router_addresses`
- Token {token} is not an input token in the configured `allow
- Certificate path is not a directory: {}
- Client certificate or private key missing in {} but client a
- Verified chain ID does not match the transaction chain
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/398f9edc95a114dc.
Report an issue: GitHub.