nanocoai/nanoclaw · error · Error
url must use HTTPS (plain HTTP is allowed only for localhost
Error message
url must use HTTPS (plain HTTP is allowed only for localhost and host.docker.internal)
What it means
The MCP server url parsed successfully but its protocol is not https:. Plain http is permitted only for loopback hosts (localhost, 127.0.0.1, [::1], host.docker.internal) because container-to-host traffic is the exception; everything else must be TLS-encrypted.
Source
Thrown at src/container-config.ts:159
const instructions = input.instructions;
if (instructions !== undefined && typeof instructions !== 'string') {
throw new Error('MCP instructions must be a string');
}
if (url !== undefined) {
if (command !== undefined) throw new Error('Provide exactly one of command or url');
if (input.args !== undefined || input.env !== undefined || input.cwd !== undefined) {
throw new Error('args, env, and cwd are only valid with command');
}
let parsed: URL;
try {
parsed = new URL(url);
} catch (err) {
throw new Error('url must be a valid HTTP(S) URL', { cause: err });
}
const loopback = ['localhost', '127.0.0.1', '[::1]', 'host.docker.internal'].includes(parsed.hostname);
if (parsed.protocol !== 'https:' && !(parsed.protocol === 'http:' && loopback)) {
throw new Error('url must use HTTPS (plain HTTP is allowed only for localhost and host.docker.internal)');
}
if (parsed.username || parsed.password || parsed.hash) {
throw new Error('url must not contain credentials or fragments; use OneCLI for authentication');
}
for (const key of parsed.searchParams.keys()) {
if (SECRET_QUERY_KEY_RE.test(key.replace(CAMEL_SPLIT_RE, '$1_$2'))) {
throw new Error(`url query parameter "${key}" looks like a credential; use OneCLI for authentication`);
}
}
const headers = parseStringRecord(input.headers, 'headers');
return {
type: 'http',
url,
...(headers === undefined ? {} : { headers }),
...(instructions === undefined ? {} : { instructions }),
};
}
if (command === undefined) throw new Error('Provide exactly one of command or url');View on GitHub (pinned to 294ef2aee8)
Solutions
- Use an https:// url (put the server behind TLS or a reverse proxy)
- If the server runs on the Docker host, switch the hostname to host.docker.internal so plain http is allowed
- For other LAN hosts, add TLS via a local proxy (e.g. caddy/nginx) and use https
Example fix
// before
{"url":"http://192.168.1.5:8080/mcp"}
// after
{"url":"http://host.docker.internal:8080/mcp"} Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(entry.url); const loopback = ['localhost','127.0.0.1','[::1]','host.docker.internal'].includes(u.hostname); if (u.protocol !== 'https:' && !(u.protocol === 'http:' && loopback)) throw new UserError('use https or a loopback host'); Type guard
function isAllowedMcpUrl(s: string): boolean { const u = new URL(s); const lo = ['localhost','127.0.0.1','[::1]','host.docker.internal'].includes(u.hostname); return u.protocol === 'https:' || (u.protocol === 'http:' && lo); } Try / catch
catch (err) { if (err.message.includes('must use HTTPS')) suggestTlsOrLoopback(); else throw err; } Prevention
- Default to https for any remote MCP endpoint
- Use host.docker.internal for host-local servers
When it happens
Trigger: An http:// url with a non-loopback hostname, e.g. http://mcp.internal.example.com:8080/mcp; also http:// with a LAN IP like 192.168.1.5.
Common situations: Pointing at an internal HTTP-only server on the network; dev setup moved from localhost to a LAN host; port-forwarded service without TLS.
Related errors
- url must not contain credentials or fragments; use OneCLI fo
- url query parameter "${key}" looks like a credential; use On
- cwd escapes the plugin root
- ${settingsFile} hooks.SessionStart must be an array
- --name is required
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/6a7d1d95671816f1.
Report an issue: GitHub.