{"record":{"id":"e13edf79420e165b","repo":"cloudflare/cloudflared","slug":"s-doesn-t-have-a-hostname-consider-adding-a-sche","errorCode":null,"errorMessage":"%s doesn't have a hostname, consider adding a scheme","messagePattern":"(.+?) doesn't have a hostname, consider adding a scheme","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/cloudflared/tunnel/ingress_subcommands.go","lineNumber":131,"sourceCode":"\t\treturn nil, errors.New(\"No configuration file was found. Please create one, or use the --config flag to specify its filepath. You can use the help command to learn more about configuration files\")\n\t}\n\tfmt.Println(\"Validating rules from\", conf.Source())\n\treturn conf, nil\n}\n\n// testURLCommand checks which ingress rule matches the given URL.\nfunc testURLCommand(c *cli.Context) error {\n\trequestArg := c.Args().First()\n\tif requestArg == \"\" {\n\t\treturn errors.New(\"cloudflared tunnel rule expects a single argument, the URL to test\")\n\t}\n\n\trequestURL, err := url.Parse(requestArg)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s is not a valid URL\", requestArg)\n\t}\n\tif requestURL.Hostname() == \"\" && requestURL.Scheme == \"\" {\n\t\treturn fmt.Errorf(\"%s doesn't have a hostname, consider adding a scheme\", requestArg)\n\t}\n\n\tconf := config.GetConfiguration()\n\tfmt.Println(\"Using rules from\", conf.Source())\n\ting, err := ingress.ParseIngress(conf)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"Validation failed\")\n\t}\n\n\t_, i := ing.FindMatchingRule(requestURL.Hostname(), requestURL.Path)\n\tfmt.Printf(\"Matched rule #%d\\n\", i)\n\tfmt.Println(ing.Rules[i].MultiLineString())\n\treturn nil\n}\n","sourceCodeStart":113,"sourceCodeEnd":146,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/cmd/cloudflared/tunnel/ingress_subcommands.go#L113-L146","documentation":"After url.Parse succeeds, testURLCommand additionally requires the parsed URL to have a hostname or a scheme. A bare string like 'example.com' parses successfully but yields no hostname or scheme, so this error suggests adding a scheme. It guards against testing ingress rules against ambiguous host-only inputs.","triggerScenarios":"Running `cloudflared tunnel ingress url example.com` — url.Parse succeeds but Hostname() == \"\" and Scheme == \"\".","commonSituations":"Testing a ingress hostname rule without a scheme prefix; forgetting http:// or https:// when copy-pasting domains; documenting examples that use bare hostnames.","solutions":["Prefix the argument with a scheme: `cloudflared tunnel ingress url https://example.com`","Include the full origin URL you expect requests to match, e.g. `http://localhost:8080/path`","If you only meant to test hostname matching, remember the command needs scheme+host to mirror a real request"],"exampleFix":"// before\n$ cloudflared tunnel ingress url example.com\n\n// after\n$ cloudflared tunnel ingress url https://example.com","handlingStrategy":"validation","validationCode":"u, err := url.Parse(arg)\nif err == nil && u.Hostname() == \"\" && u.Scheme == \"\" {\n    arg = \"https://\" + arg // coerce host-only input\n}","typeGuard":"func hasHostOrScheme(s string) bool { u, err := url.Parse(s); return err == nil && (u.Hostname() != \"\" || u.Scheme != \"\") }","tryCatchPattern":"if u.Hostname() == \"\" && u.Scheme == \"\" {\n    return fmt.Errorf(\"add a scheme to %q, e.g. https://%s\", arg, arg)\n}","preventionTips":["Habitually include https:// when referencing origins in cloudflared commands and configs","Prefer absolute http://localhost:PORT URLs when testing local ingress rules","Share full-URL examples in runbooks, not bare hostnames"],"tags":["cli","url","ingress","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}