BotID cross domain - isBot always true

Hey! Trying to set up BotID between multiple project, both hosted on Vercel.

There’s a nextjs app router project and a POST /v1/contact endpoint and setting up BotID here works great.

But the other project is an older frontend only Gatsby site, which uses the same endpoint, but always fails with isBot: true. Tried to debug with all the AI in the world, so I’m pretty confident that the setup is correct. Here are the details:

Gatsby site has the necessary rewrites and headers in vercel.json, then in gatsby-browser.js I have this script:

import { initBotId } from 'botid/client/core'

export const onInitialClientRender = () => {
  initBotId({
    protect: [
      {
        path: '/v1/contact',
        method: 'POST',
      },
    ],
  })
}

It seems to work fine, I see the request for the botid js file and the x-is-human header gets attached. Logging out the x-is-human, x-path and x-method - they all seem correct.

On the server side I have withBotId in the next config and this code in the handler:

const verification = await checkBotId({
  advancedOptions: {
    extraAllowedHosts: ["olddomain.com", "www.olddomain.com"],
  },
});

And again, submitting to this exact endpoint from next frontend works.

Tried to change extraAllowedHosts to add / remove the www version, add / remove https version, every combination, but nothing seems to work. And there’s no reason or explanation what might be wrong. Here’s the verification logged out:

verification: {
isHuman: false,
isBot: true,
isVerifiedBot: false,
verifiedBotName: undefined,
verifiedBotCategory: undefined,
bypassed: false,
classificationReason: undefined
}

Any idea what might be wrong?

1 Like

As context we’re getting hundreds of bot submissions in the last couple of days, that’s why we started to set this up. Later I found this “Bot Protection” in the firewall rules section:

Would that achieve the same thing? Is it worth setting up BotID as well? What’s the difference? Thanks!

It looks like they don’t support cross-origin setup. I have the same problem, and I’ve debugged the window.fetch function, and it fails at these lines:

window.fetch = async (r, o) => {
        let c = new URL(r instanceof Request ? r.url : r,location.href)
          , l = c.origin === location.origin
          , d = o?.method ?? (r instanceof Request ? r.method : "GET")
          , p = t.find(u => {
            let y = R({
                path: c.pathname,
                pattern: w(u.path)
            })
              , L = u.method.toLowerCase() === d.toLowerCase() || u.method === "*";
            return y && L
        }
        );
        if (p == null || !l)
            return s(r, o); <----- it returns the origin/unprotected request
        let h = await i.getChallenge()
          , n = new Headers(o?.headers || (r instanceof Request ? r.headers : void 0));
        ...

Here is how these lines look in unminified format:

window.fetch = async (input, init) => {
  const url = new URL(input instanceof Request ? input.url : input, location.href);
  const sameOrigin = url.origin === location.origin;
  const method = init?.method ?? (input instanceof Request ? input.method : "GET");

  const route = routes.find(r => {
    const matchesPath = f({ path: url.pathname, pattern: e(r.path) });
    const matchesMethod =
      r.method.toLowerCase() === method.toLowerCase() || r.method === "*";
    return matchesPath && matchesMethod;
  });

  if (!route || !sameOrigin) {
    return originalFetch(input, init); <----- it returns the origin/unprotected request, because it's not same origin 
  }

  const challenge = await challengeManager.getChallenge();
  ...
};

Or was you able to figure out how to make it work?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.