If your site has a field that asks for a website address, and that field is an input with type="url", there is a fair chance that a large share of the people and agents who use it are typing a value the browser refuses, and that nobody on your side knows. The field wants https://yourcompany.com. The placeholder says yourcompany.com. The browser considers the second one invalid, blocks submission, and shows a tooltip. Nothing is sent to your server, so nothing is logged, so nothing is noticed.
We found this on our own scan form, the single most important form on the site, during the checks we run before every release. This post explains what type="url" actually validates, why the failure is silent, what we changed, and the general rule it taught us about where validation belongs.
What the browser is checking
type="url" is one of the input types that carries native constraint validation. Per the HTML specification, a value in a URL field must be a valid absolute URL: it needs a scheme. https://example.com passes. example.com does not, because without a scheme it is not a URL at all, it is a hostname. MDN's reference for the input element documents this under the url type, along with the pattern and required attributes that stack on top of it.
When a form is submitted, the browser runs constraint validation over every control. If any control is invalid, the submission is cancelled, the first invalid control is focused, and the browser draws a small message next to it. That is the entire failure mode: a cancelled event and a tooltip. There is no request, no error page, no entry in your analytics, no line in your server log.
Everything about the design of that field encourages the invalid value. Nobody writes https:// when they say their website out loud. Most placeholders show the bare domain because that is what the field looks best with. Autocomplete often offers the bare domain. The browser then rejects the value the page itself suggested.
Why an agent is worse off than a person
A person eventually sees the tooltip, adds the scheme and moves on, mildly annoyed. An agent has two ways of using a form, and type="url" defeats both.
An agent driving a real browser fills the field and activates the submit button, exactly as a form it can fill should allow. The browser cancels the submission. The agent observes that nothing changed: same URL, same page, no navigation. Depending on how it was built it retries, gives up, or reports that the form does not work. The validation message is drawn by the browser's own UI and is not part of the page, so the agent has no text to read that would tell it what went wrong.
An agent that does not drive a browser reads the form's action and method and sends the request itself. Native validation never runs, so the bare domain reaches your server, and whether the server accepts it depends on whether anyone ever wrote the server-side handling, or whether they assumed the browser had already done it. Very often the server rejects the same value for the same reason, and now the failure is a 400 rather than a tooltip. Either way, the field's contract was never written down anywhere an agent could read it.
What we do instead
Our scan form uses type="text" with inputMode="url", and does its normalisation on the server. The component carries a comment explaining the choice, because it looks wrong to anyone who has been taught to use the most specific input type available.
<label for="scan-url" class="sr-only">Your website address</label>
<input
id="scan-url"
name="url"
type="text"
inputmode="url"
autocomplete="url"
spellcheck="false"
required
placeholder="yourcompany.com"
>
<button type="submit">Scan my site</button>
inputmode="url" gives a phone the URL keyboard, with the slash and the full stop close to hand, which is the one real benefit of type="url" that we wanted to keep. autocomplete="url" lets the browser offer a stored value. required still applies, so an empty field is refused, which is the only client-side refusal we think is safe: an empty value cannot be fixed by normalising it. Everything else is accepted and sent.
On the server, the value goes through the same normaliseDomain() function that the report URLs, the leaderboard and our seed tooling use, because the identity of a site has to be one rule everywhere. It trims and lowercases the input, prepends https:// if no scheme is present, parses the result as a URL, takes the hostname, drops a trailing dot and a leading www., and then checks that what remains looks like a public hostname. Any of these are accepted and produce the same result:
yourcompany.com
YourCompany.com/
https://www.yourcompany.com/pricing?utm_source=x
http://yourcompany.com
If the value cannot be normalised, because it is an IP address, a single word, or something that is not a hostname at all, the server redirects back to the form with an error code and the original value, so the page can say what was wrong in text that is part of the document. An agent that reads the response sees the problem. A browser tooltip would have shown it to nobody.
Client-side validation versus server-side normalisation
The general lesson is older than agents, but agents make it expensive to ignore. Client-side validation is a convenience for people: it saves a round trip when the input is obviously wrong. It is not a contract, because anything that submits the form without running your JavaScript or the browser's constraint checks bypasses it entirely. Server-side handling is the contract, and it runs for everyone.
Once you accept that, two design rules follow.
First, decide for each field whether a malformed value should be refused or repaired. A missing scheme on a URL is repairable. Mixed case in an email address is repairable. Spaces in a phone number are repairable. Refusing values that could have been repaired is a choice to lose the submission, and the loss is invisible because refused submissions never reach you. Only refuse what you genuinely cannot interpret.
Second, when you do refuse, put the reason in the document. A type="email" tooltip, a pattern mismatch bubble, a setCustomValidity message: all of these are drawn by the browser outside the page and are invisible to anything that reads the page. Re-render the form with the error as text next to the field, keep the submitted value in place, and an agent can read the message and try again. This is also what check C1 is measuring from the other direction. It looks for a label, a correct type and a real submit button, and every one of those is a piece of the field's contract that lives in the HTML rather than in behaviour.
That does not make the specific types useless. type="email", type="tel" and type="number" are what the scanner expects where a field's name calls for them, and they are the right choice for those fields because what they reject, an empty string in a required email or a letter in a number, is not something you would want to repair. type="url" is different only because the thing it rejects is the thing people type.
Check your own site
Run the free scan and look at the C1 evidence on your report, then try the field yourself with a bare domain and see whether it submits. The check's definition is under actionability on the methodology page, and if the fix turns out to be more than a one-attribute change, the implementation service does this kind of work.