abhigyanpatwari/GitNexus · error · Error
Insecure http:// LLM base URLs are only allowed for…
Error message
Insecure http:// LLM base URLs are only allowed for localhost/127.0.0.1 or hosts listed by --allow-insecure-connection / GITNEXUS_ALLOW_INSECURE_CONNECTION. Use https:// for remote endpoints (got ${parsed.origin}) What it means
The base URL uses http:// (plaintext) and its host is not localhost, 127.0.0.1, or ::1, and is not in the allowlist built from `--allow-insecure-connection` / GITNEXUS_ALLOW_INSECURE_CONNECTION. By default plaintext LLM traffic is restricted to loopback to prevent leaking API keys and to block SSRF against internal hosts; remote http:// endpoints must be explicitly opted into per hostname. The message shows parsed.origin (scheme+host+port, no credentials).
Solutions
- Best: switch the endpoint to https:// (add TLS/terminating proxy) — no allowlist needed
- Otherwise allowlist the exact host: `--allow-insecure-connection 192.168.1.10` or GITNEXUS_ALLOW_INSECURE_CONNECTION=llm.internal.corp
- If the server is actually on this machine, use http://localhost:<port> or http://127.0.0.1:<port> which are always allowed
- Confirm the allowlisted hostname matches the base URL host character-for-character (no port, no scheme, no path)
Example fix
# before gitnexus wiki --provider custom --base-url http://192.168.1.10:8080/v1 # after gitnexus wiki --provider custom --base-url http://192.168.1.10:8080/v1 --allow-insecure-connection 192.168.1.10
Defensive patterns
Strategy: validation
Validate before calling
function assertInsecureHttpAllowed(baseUrl: string, allowlist: readonly string[]): void {
const u = new URL(baseUrl);
if (u.protocol !== 'http:') return;
const host = u.hostname.toLowerCase().replace(/^\[|\]$/g, '');
if (host === 'localhost' || host === '127.0.0.1' || host === '::1') return;
if (!allowlist.map(h => h.trim().toLowerCase()).includes(host)) {
throw new Error(`http://${host} not allowlisted — use https or add --allow-insecure-connection ${host}`);
}
} Try / catch
try {
validateLLMBaseUrl(baseUrl, allowHosts);
} catch (err) {
if (err instanceof Error && err.message.includes('Insecure http:// LLM base URLs')) {
// extract host via new URL(baseUrl).hostname and prompt user to allowlist it or upgrade to https
}
} Prevention
- Prefer https endpoints; treat the allowlist as an exception mechanism only
- Store allowlist entries in config next to the base URL so they stay in sync
- For LAN inference boxes, terminate TLS locally (e.g. Caddy/nginx) instead of allowlisting plaintext
When it happens
Trigger: Setting `--base-url http://192.168.1.10:8080/v1` or `http://llm.internal.corp/v1` for a LAN/self-hosted LLM (Ollama, LiteLLM proxy, vLLM on another machine) without a matching allowlist entry; hostname comparison fails after normalization (host:port entries in the allowlist are rejected upstream, trailing-case mismatches are handled but a different host string is not).
Common situations: Self-hosted LLM on a LAN box or Docker host (http://host.docker.internal, NAS IP); corporate internal endpoint that is http-only; forgetting that the allowlist matches the hostname exactly, not a domain suffix.
Related errors
- --allow-insecure-connection /…
- AZURE_DEVOPS_URL is configured over cleartext http:// — the…
- Invalid LLM base URL: must be a well-formed http:// or…
- LLM base URL must use http:// or https://
- LLM request timed out after
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/44c6c6d7c9c42267.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/wiki/llm-client.ts:262
parsed = new URL(baseUrl);
} catch {
// Do not include the raw input in the message — it may contain credentials.
throw new Error('Invalid LLM base URL: must be a well-formed http:// or https:// URL');
}
if (!['https:', 'http:'].includes(parsed.protocol)) {
// Use parsed.protocol only (scheme), not the full URL, to avoid leaking credentials.
throw new Error(`LLM base URL must use http:// or https:// (got ${parsed.protocol})`);
}
if (parsed.protocol === 'http:') {
// Node's URL parser preserves IPv6 brackets in hostname (e.g. "[::1]"),
// so strip them before comparing to bare address literals.
const host = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, '');
const allowedHosts = new Set(allowedInsecureHttpHosts.map(normalizeAllowedInsecureHttpHost));
if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1' && !allowedHosts.has(host)) {
// Use parsed.origin (scheme+host+port, no credentials) instead of the full URL.
throw new Error(
`Insecure http:// LLM base URLs are only allowed for localhost/127.0.0.1 ` +
`or hosts listed by --allow-insecure-connection / ${LLM_ALLOW_INSECURE_CONNECTION_ENV}. ` +
`Use https:// for remote endpoints (got ${parsed.origin})`,
);
}
}
}
/**
* Returns true if the given base URL is an Azure OpenAI endpoint.
* Uses proper hostname matching to avoid spoofed URLs like
* "https://myresource.openai.azure.com.evil.com/v1".
*/
export function isAzureProvider(baseUrl: string): boolean {
try {
const { hostname } = new URL(baseUrl);
return hostname.endsWith('.openai.azure.com') || hostname.endsWith('.services.ai.azure.com');
} catch {View on GitHub (pinned to 52924ef12c)