{"record":{"id":"ea7264c1545185fc","repo":"jdx/mise","slug":"remote-cache-url-must-use-https-except-for-loopbac","errorCode":null,"errorMessage":"remote cache URL must use HTTPS except for loopback development servers","messagePattern":"remote cache URL must use HTTPS except for loopback development servers","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":809,"sourceCode":"    } else {\n        bail!(\"GitHub Actions OIDC request URL must use HTTPS\")\n    }\n}\n\nfn validate_remote_url(base_url: &Url, authenticated: bool) -> Result<()> {\n    if base_url.scheme() == \"https\" {\n        return Ok(());\n    }\n    if base_url.scheme() != \"http\" {\n        bail!(\"remote cache URL must use HTTPS\");\n    }\n    let is_loopback = base_url.host().is_some_and(|host| match host {\n        Host::Domain(host) => host.eq_ignore_ascii_case(\"localhost\"),\n        Host::Ipv4(address) => address.is_loopback(),\n        Host::Ipv6(address) => address.is_loopback(),\n    });\n    if !is_loopback && authenticated {\n        bail!(\"remote cache URL must use HTTPS except for loopback development servers\");\n    }\n    if !is_loopback {\n        warn!(\n            \"using an unauthenticated remote build cache over plain HTTP; cache traffic can be read \\\n             or modified in transit\"\n        );\n    }\n    Ok(())\n}\n\nfn normalized_base_url(mut url: Url) -> Url {\n    if !url.path().ends_with('/') {\n        url.set_path(&format!(\"{}/\", url.path()));\n    }\n    url\n}\n\nfn retry_delays(retries: i64) -> impl Iterator<Item = Duration> {","sourceCodeStart":791,"sourceCodeEnd":827,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/crates/mise-cache-core/src/lib.rs#L791-L827","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Serve the cache over https (add TLS at the server or expose it through an https ingress and give the client that URL)","For a local dev server, point base_url at a loopback host (localhost, 127.0.0.1, ::1) where http with credentials is accepted","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)"],"exampleFix":"# before\nbase_url = \"http://cache.corp.internal\"\ntoken = \"secret\"          # authenticated over plain http -> rejected\n\n# after\nbase_url = \"https://cache.corp.internal\"\ntoken = \"secret\"","handlingStrategy":"validation","validationCode":"fn cache_url_allows_auth(url: &url::Url) -> bool {\n    if url.scheme() == \"https\" {\n        return true;\n    }\n    if url.scheme() != \"http\" {\n        return false;\n    }\n    url.host().is_some_and(|h| match h {\n        url::Host::Domain(h) => h.eq_ignore_ascii_case(\"localhost\"),\n        url::Host::Ipv4(a) => a.is_loopback(),\n        url::Host::Ipv6(a) => a.is_loopback(),\n        _ => false,\n    })\n}\n\n// before building an authenticated RemoteCacheConfig\nanyhow::ensure!(\n    cache_url_allows_auth(&config.base_url),\n    \"refusing to send credentials over plain http to {}\",\n    config.base_url\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default cache URLs to https in all environments except loopback dev","Terminate TLS at an ingress the client can reach via its https URL","Use loopback hosts (localhost/127.0.0.1/::1) for local authenticated dev servers"],"tags":["https","security","remote-cache","authentication","configuration"],"backgroundTag":"credentials-over-plain-http","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}