cloudflare/cloudflared · error

is not a valid URL

Error message

%s is not a valid URL

What it means

testURLCommand (backing `cloudflared tunnel ingress url <arg>`) parses the argument as a URL to test against ingress rules. If net/url.Parse fails on the argument, this error reports the argument as an invalid URL. It catches malformed inputs that cannot be parsed at all.

Solutions

  1. Quote the URL argument in the shell to avoid stray characters: `cloudflared tunnel ingress url 'https://example.com'`
  2. Use a well-formed URL with a scheme, e.g. `https://example.com/path`
  3. If the argument parses but has no host/scheme, add `https://` in front (see the related 'doesn't have a hostname' error)

Example fix

// before
$ cloudflared tunnel ingress url http://example.com?redirect=http://other.com&a=|b

// after (quote the argument)
$ cloudflared tunnel ingress url 'http://example.com?redirect=http://other.com&a=|b'
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(arg)
if err != nil {
    return fmt.Errorf("invalid URL %q: %w", arg, err)
}

Type guard

func isParsableURL(s string) bool { _, err := url.Parse(s); return err == nil }

Try / catch

_, err := url.Parse(requestArg)
if err != nil {
    return fmt.Errorf("cannot test %q: %w", requestArg, err)
}

Prevention

When it happens

Trigger: Running `cloudflared tunnel ingress url <arg>` where url.Parse rejects the argument, e.g. it contains control characters or an unparseable malformed scheme.

Common situations: Shell quoting issues injecting stray characters into the argument; pasting a URL with invisible whitespace; passing an escaped or partially-encoded URL.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/a23f8e2fedbd9855. Report an issue: GitHub.

Appendix: source

Thrown at cmd/cloudflared/tunnel/ingress_subcommands.go:128

	}
	conf = config.GetConfiguration()
	if conf.Source() == "" {
		return 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")
	}
	fmt.Println("Validating rules from", conf.Source())
	return conf, nil
}

// testURLCommand checks which ingress rule matches the given URL.
func testURLCommand(c *cli.Context) error {
	requestArg := c.Args().First()
	if requestArg == "" {
		return errors.New("cloudflared tunnel rule expects a single argument, the URL to test")
	}

	requestURL, err := url.Parse(requestArg)
	if err != nil {
		return fmt.Errorf("%s is not a valid URL", requestArg)
	}
	if requestURL.Hostname() == "" && requestURL.Scheme == "" {
		return fmt.Errorf("%s doesn't have a hostname, consider adding a scheme", requestArg)
	}

	conf := config.GetConfiguration()
	fmt.Println("Using rules from", conf.Source())
	ing, err := ingress.ParseIngress(conf)
	if err != nil {
		return errors.Wrap(err, "Validation failed")
	}

	_, i := ing.FindMatchingRule(requestURL.Hostname(), requestURL.Path)
	fmt.Printf("Matched rule #%d\n", i)
	fmt.Println(ing.Rules[i].MultiLineString())
	return nil
}

View on GitHub (pinned to 2253eeeb25)