Invalid Domain for Site Key: The reCAPTCHA Error Your Visitors See and You Don’t

No Comments

Photo of author

By Liu Yu

ERROR for site owner: Invalid domain for site key is an unusual failure, because of who sees it. It renders in your visitor’s browser, in the box where the captcha should be. It does not appear in your admin dashboard, your error log, or your uptime monitor. If you never load your own contact page as an anonymous visitor and actually submit it, you can go weeks without knowing.

We know the number of weeks because we did exactly that. More on our own case below, including the part where we caused it.

What the error actually means

A reCAPTCHA site key is registered against a list of allowed domains. When the widget loads, Google compares the page’s hostname against that list. If it does not match, the widget refuses to issue a token and displays the error to whoever is standing in front of it.

Your form then fails in one of two ways, and the second is worse:

  • The visitor sees the error, cannot complete the captcha, and gives up. You get nothing and no signal.
  • Or the widget is invisible (reCAPTCHA v2 invisible, or v3), so the visitor sees a normal form, fills it in, clicks send — and the submission is rejected server-side because the captcha token is missing. Often with a generic message, sometimes with a spinner that never resolves.

In the second case there is nothing to notice. The form looks fine. It has simply stopped producing enquiries.

What the domain list actually covers

Start with the rule, because it is the opposite of what a lot of advice on this error assumes. Google’s documentation states that “the API key pair is unique to the domains and first-level subdomains that you specify” — registering example.com does cover www.example.com and other first-level subdomains.

So if your key lists the apex domain and you are still getting this error, the domain list is probably not your problem, and the checks below matter more than adding hostnames.

Two things the coverage rule does not give you:

  • It does not work upward. Registering www.example.com alone does not cover the apex, or a different subdomain.
  • It does not cover a different registrable domain. A staging host on a separate domain, a country domain, or a migration to a new name all need their own entry.

It is still worth knowing which host visitors actually land on, because the checks further down depend on testing the right one:

curl -sI https://example.com/ | grep -i "^location:"
curl -sI https://www.example.com/ | grep -i "^location:"

Whichever one does not redirect is your real landing host. That is the one the site key must include. If you run paid traffic, check the final URLs in your campaigns too — they may land somewhere different from your organic pages.

Other common mismatches, in rough order of how often we see them:

  • Staging and production share a key. The key lists the staging host and production was never added, or the reverse.
  • A subdomain was never registered. Registering example.com covers subdomains only if you rely on Google’s subdomain matching; it is a setting, not a guarantee, and it can be off.
  • The site moved to a new domain and the key was never updated.
  • “Verify the origin of reCAPTCHA solves” was toggled. With it off, domain checking is skipped entirely; with it on, it is strict. A key that worked yesterday can start failing after somebody changes that checkbox, with no other change on the site.

Verify a site key without deploying anything

This is the part most troubleshooting articles leave out. You do not have to push a change and hope. Google’s own widget endpoint will tell you whether a key works for a given host.

The widget URL takes the host as a base64-encoded origin. Build it, open it in a browser, and read the answer:

# 1. Encode the origin you want to test, including the port
printf 'https://www.example.com:443' | base64

# 2. Open this URL with your key and that encoded value
https://www.google.com/recaptcha/api2/anchor?ar=1&k=YOUR_SITE_KEY&co=ENCODED_ORIGIN&hl=en&size=invisible

If the key is rejected for that host you get the same Invalid domain for site key message your visitors would see, without touching the site.

Two limits worth stating plainly. This is an internal component endpoint, not a supported diagnostic API — it can change without notice. And a widget that renders proves only that the domain check passed; it does not prove a token will be issued, or that your server-side verification will accept it. Treat a failure here as informative and a success as necessary but not sufficient.

The check that is sufficient is further down: submit the real form and confirm the message arrives.

What it cost us: twelve days, 207 paid clicks, zero enquiries

We manage a B2B export site that runs Google Ads to its contact forms. Enquiries stopped. Not dropped — stopped.

Every form on the site returned the domain error, Google refused to issue a token, and the server rejected each submission for a blank captcha field. It failed silently for about twelve days, during which the ad account delivered 207 clicks and produced zero enquiries, spending roughly a thousand yuan on traffic that had nowhere to land.

A correction, because we got the root cause wrong in our own write-up at the time. We recorded it as “the key listed the apex domain but not www”. Under the coverage rule quoted above that explanation does not hold — the apex should have covered the www host. We did not preserve the console state, the exact key loaded by the page, or the server-side verification response, so we cannot now distinguish between the plausible alternatives: a different key being loaded than the one we checked, a console change that had not taken effect, a product-type mismatch, or a domain entry that was not what we believed it was.

What the incident does establish is the failure shape — silent, visitor-side, invisible to the dashboard — and the fact that we never ran the one test that would have caught it regardless of cause. The specific configuration error is the part we can no longer prove.

Three things about how we handled it are worth being explicit about, because they are the actual lesson:

We proposed the reCAPTCHA rollout, wrote the deployment document, and never once tested a real submission on the live host. The configuration was checked against the domain we had in our heads rather than the one visitors reach.

Partway through the outage we “finished the job.” One popup form had not yet had reCAPTCHA added. We added it — closing the last remaining channel that was still producing enquiries.

When the client said results looked worse than before, we went to the analytics. We pulled campaign data, calculated cost per acquisition, ran significance tests on the conversion drop. What we did not do was open the site and submit the form. That cost two more days on top of the ten.

The diagnostic that would have found it in ninety seconds costs nothing: fill in your own form, as an anonymous visitor, and confirm the message arrives.

Why the DOM check told us everything was fine

Automated checking made this worse rather than better. Inspecting the page finds the reCAPTCHA script tag, the widget container and the form markup — all present, all correct. The failure happens after that, when Google declines to issue a token and the server rejects a submission with an empty captcha field.

Markup being present is not evidence that a form works. The same applies to the “send test email” button inside form plugins: it verifies the mail path while skipping the form, the captcha and the validation entirely, so it passes cleanly on a site whose form has been dead for a fortnight.

The only test that proves anything is a real submission that reaches a real inbox. Everything cheaper than that is a proxy, and this failure mode defeats every proxy we had in place.

The grouping trick that located it in one look

Once we finally looked at submissions rather than at spend, the shape of the data identified the cause immediately, because the site happened to contain its own control group.

Forms Enquiries before Enquiries during
Four forms with reCAPTCHA 19 0
One popup form without it 2 2

The forms that shared one attribute all went to zero; the one that lacked it behaved normally. That contrast is the finding. We are deliberately not attaching a p-value to it: doing so requires the exposure and the time window on both sides to be comparable, and ours were not measured that way. A significance figure computed from mismatched baselines looks rigorous and proves nothing — the useful evidence here is that one group with a shared component went to zero while the group without it did not.

The general version of this is worth keeping: when a metric collapses, split it by whatever the affected things have in common rather than looking at the total. A site-wide conversion rate cannot distinguish “demand fell” from “one component broke”, but a split by form, by template, by language or by landing page usually can — and it costs one query.

A checklist for adding a captcha to a form that currently works

Ordered so that the cheap checks come before the expensive ones:

  1. Establish the real landing host with curl -sI on both apex and www, and check the final URLs of any ad campaigns.
  2. Register both hosts on the site key, plus staging if it shares the key.
  3. Verify the key against each host using the widget URL above, before deploying anything.
  4. Deploy to one form first, never to all of them at once. Keep at least one working channel while you confirm.
  5. Submit that form as an anonymous visitor, from a private window, and confirm the message arrives in the destination inbox.
  6. Only then roll out to the remaining forms, re-testing each one.
  7. Add a standing check — the failure is silent, so it needs an active monitor rather than an absence of complaints.

Step four is the one we skipped and the one that would have contained the damage. Rolling a front-end validation change out to every form simultaneously removes your control group and your fallback in a single move.

Monitoring something that fails silently

An outage nobody can see needs a monitor that submits, not one that pings. What we run now on that site, hourly:

  • Re-validate the site key against the live landing host whenever a captcha is detected on a form, using the widget check above.
  • Submit a probe with required fields deliberately empty and confirm the server responds with the expected validation error — a form that has stopped processing entirely answers differently from one that is correctly rejecting bad input.
  • Alert if no enquiry has arrived in five days, since on this site that is already abnormal.
  • Push new enquiries to a chat channel, so the absence of messages is itself visible.

We also tested the monitor against a mocked failure to confirm it actually fires. An alarm that has never been shown to alarm is not a control — that was the other half of what went wrong here.

If your enquiries dropped rather than stopped, the cause is more likely somewhere in the mail path than in the captcha — we work through the four layers a WordPress email can die at separately. And if things broke around a server move, these are the things that break quietly after a migration.

We handle this kind of diagnosis as part of ongoing WordPress support, mostly for export businesses whose enquiry forms are the whole funnel. If your forms went quiet and you are not certain they still work, send us the URL — the first thing we do is submit one.

Leave a Comment