jdx/mise · error · eyre::Report

remote cache URL must use HTTPS except for loopback developm

Error message

remote cache URL must use HTTPS except for loopback development servers

What it means

validate_remote_url rejects plain-http base URLs on non-loopback hosts whenever the client is authenticated — i.e. a non-empty token, a token_file, or an oidc_audience is configured. This prevents bearer credentials from crossing the network in cleartext. Unauthenticated plain http to a non-loopback host is allowed but logs a warning that cache traffic can be read or modified in transit.

Source

Thrown at crates/mise-cache-core/src/lib.rs:809

    } else {
        bail!("GitHub Actions OIDC request URL must use HTTPS")
    }
}

fn validate_remote_url(base_url: &Url, authenticated: bool) -> Result<()> {
    if base_url.scheme() == "https" {
        return Ok(());
    }
    if base_url.scheme() != "http" {
        bail!("remote cache URL must use HTTPS");
    }
    let is_loopback = base_url.host().is_some_and(|host| match host {
        Host::Domain(host) => host.eq_ignore_ascii_case("localhost"),
        Host::Ipv4(address) => address.is_loopback(),
        Host::Ipv6(address) => address.is_loopback(),
    });
    if !is_loopback && authenticated {
        bail!("remote cache URL must use HTTPS except for loopback development servers");
    }
    if !is_loopback {
        warn!(
            "using an unauthenticated remote build cache over plain HTTP; cache traffic can be read \
             or modified in transit"
        );
    }
    Ok(())
}

fn normalized_base_url(mut url: Url) -> Url {
    if !url.path().ends_with('/') {
        url.set_path(&format!("{}/", url.path()));
    }
    url
}

fn retry_delays(retries: i64) -> impl Iterator<Item = Duration> {

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Serve the cache over https (add TLS at the server or expose it through an https ingress and give the client that URL)
  2. For a local dev server, point base_url at a loopback host (localhost, 127.0.0.1, ::1) where http with credentials is accepted
  3. If the cache is genuinely unauthenticated, remove the token/token_file/oidc_audience so the client is not classified as authenticated (a tampering warning is still logged)

Example fix

# before
base_url = "http://cache.corp.internal"
token = "secret"          # authenticated over plain http -> rejected

# after
base_url = "https://cache.corp.internal"
token = "secret"
Defensive patterns

Strategy: validation

Validate before calling

fn cache_url_allows_auth(url: &url::Url) -> bool {
    if url.scheme() == "https" {
        return true;
    }
    if url.scheme() != "http" {
        return false;
    }
    url.host().is_some_and(|h| match h {
        url::Host::Domain(h) => h.eq_ignore_ascii_case("localhost"),
        url::Host::Ipv4(a) => a.is_loopback(),
        url::Host::Ipv6(a) => a.is_loopback(),
        _ => false,
    })
}

// before building an authenticated RemoteCacheConfig
anyhow::ensure!(
    cache_url_allows_auth(&config.base_url),
    "refusing to send credentials over plain http to {}",
    config.base_url
);

Prevention

When it happens

Trigger: Configuring base_url = http://cache.corp.internal together with a token, token file, or OIDC audience; a TLS-terminating load balancer where the client-visible internal URL is http; moving a dev setup from localhost to a LAN host while keeping http and the token.

Common situations: Internal cache servers without TLS certificates; ingress topologies where TLS stops at the edge; CI configuring the token via env while the URL was only ever tested on localhost.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/ea7264c1545185fc. Report an issue: GitHub.