> ## Documentation Index
> Fetch the complete documentation index at: https://verifyngo.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting verifyngo: fix common setup problems

> Step-by-step fixes for common verifyngo problems including wrong IP detection, CAPTCHA verification failures, rules not reloading, and ban issues.

This page covers the most common issues people run into when setting up verifyngo, and how to fix them. Work through the relevant accordion below — each one describes the symptom, explains why it happens, and gives concrete steps to resolve it.

<AccordionGroup>
  <Accordion title="Every visitor is challenged, even after solving the CAPTCHA">
    If visitors keep seeing the challenge page after solving it, the problem is almost always with the verification cookie.

    * **`cookie_secret` changed.** If `cookie_secret` is different from the value used when the cookie was issued, verifyngo rejects the cookie as invalid. Make sure `cookie_secret` is the same value across every restart and config reload. If you rotated it intentionally, visitors simply need to solve the challenge once more.

    * **`cookie_name` changed.** If you renamed `cookie_name` between config reloads, the browser is still sending the old cookie name — verifyngo won't recognise it. Ensure the name is consistent, or instruct visitors to clear their cookies.

    * **Cookie domain or path mismatch.** If the browser is sending the cookie but verifyngo isn't reading it, check that the domain the visitor uses matches where the cookie was set. This can happen when you have multiple subdomains or when the proxy strips the `Cookie` header.
  </Accordion>

  <Accordion title="verifyngo is seeing the wrong IP (e.g. the proxy's IP instead of the visitor's IP)">
    By default, verifyngo uses the TCP connection's remote address as the visitor IP. When verifyngo sits behind a reverse proxy (nginx, Caddy, a load balancer), that address is the proxy's IP — not the visitor's.

    1. Set `"trust_real_ip": true` in `config.json`.
    2. Add your reverse proxy's IP address or CIDR range to `trusted_proxies`, for example:
       ```json theme={null}
       "trusted_proxies": ["10.0.0.1", "172.16.0.0/12"]
       ```
    3. Confirm your reverse proxy is forwarding `X-Forwarded-For`. Most do this by default, but double-check your proxy configuration.

    When `trust_real_ip` is enabled, verifyngo reads the first IP address from the `X-Forwarded-For` header sent by a trusted proxy. If `trusted_proxies` is omitted, verifyngo only trusts `X-Forwarded-For` from connecting addresses that are loopback or RFC-1918 private. If your proxy connects from a public IP, you must add it to `trusted_proxies` explicitly, otherwise verifyngo will ignore the `X-Forwarded-For` header and fall back to the direct connection address.
  </Accordion>

  <Accordion title="Rules or policy changes aren't being picked up">
    verifyngo hot-reloads file-based rules and policies, but there are a few things that can prevent changes from taking effect.

    * **Wait a moment.** Changes to `rules_file` or `policy_file` are detected on a \~10-second polling interval. If you just saved the file, wait up to 10 seconds and try again.

    * **`policy_file` takes priority over `rules_file`.** If both are set, only `policy_file` is used and watched for changes. Updates to `rules_file` will be ignored while `policy_file` is configured.

    * **Check for parse errors.** If the file contains a syntax or compile error, verifyngo keeps the previous valid rules active and logs a `parse/compile error` message. Check your verifyngo log output for that message, fix the file, and the reload will retry automatically.

    * **Inline rules require a restart.** Rules written directly in the `"rules"` array inside `config.json` are only loaded at startup. Only file-based rules (`rules_file`, `policy_file`) support hot-reload without a restart.
  </Accordion>

  <Accordion title="The CAPTCHA isn't verifying (visitor solves it but keeps getting challenged)">
    When a visitor solves the CAPTCHA but verifyngo still challenges them, the server-side token verification is failing.

    * **Check the logs first.** Look for `verify error` or `verify failed` in verifyngo's output. The error message usually tells you exactly what went wrong (HTTP error, network timeout, invalid secret, etc.).

    * **Cap (`provider: "cap"`):** Make sure `cap.verify_url` points to the internal address your Cap instance listens on, not its public URL. Token verification is performed server-side by verifyngo, so `verify_url` must be reachable from the verifyngo process itself (e.g. `http://127.0.0.1:3000`), not from the visitor's browser. `cap.api_url` is the public URL shown to the browser — these are two separate fields.

    * **Turnstile / hCaptcha:** Make sure `secret_key` is correct. A common mistake is swapping the site key and the secret key — only the secret key is used server-side.

    * **Network / firewall.** Check that verifyngo can reach the verify endpoint. If you're running in a container or behind strict firewall rules, the verification request may be blocked. Test with `curl` from the verifyngo host.
  </Accordion>

  <Accordion title="Static assets are being challenged">
    If CSS, JavaScript, images, or fonts are being intercepted by the challenge page, `bypass_paths` may not be covering them.

    * **Default list is already broad.** By default, verifyngo bypasses requests matching common static-asset extensions (`.css`, `.js`, `.png`, `.jpg`, `.gif`, `.svg`, `.webp`, `.avif`, `.ico`, `.woff`, `.woff2`, `.ttf`, `.otf`, `.eot`, `.wasm`, `.mp4`, `.webm`) as well as `/favicon.ico`, `/robots.txt`, `/sitemap.xml`, and `/.well-known/` paths. If you haven't overridden `bypass_paths`, these are already excluded.

    * **Custom `bypass_paths` overrides the entire default list.** If you set `bypass_paths` to a custom array, the built-in defaults are replaced — not merged. Make sure your custom list includes all extensions you need.

    * **Restore defaults.** To go back to the built-in list, remove the `bypass_paths` key from `config.json` entirely (or set it to `null`).
  </Accordion>

  <Accordion title="Visitor is immediately banned without hitting the walk-away threshold">
    If a visitor is blocked before they've had a chance to fail enough challenges, they may already be carrying an existing ban.

    * **Previous session ban.** The visitor may have been banned in an earlier session. With `store.backend: "memory"`, bans persist until verifyngo restarts. With `store.backend: "redis"` or `"file"`, bans persist across restarts until the TTL expires.

    * **Check `ban.duration`.** The default is `"24h"`. If a visitor hit the threshold yesterday, their ban is still active. You can reduce `ban.duration` or manually clear the store (restart verifyngo for the memory backend, or delete the Redis key / file for the persistent backends).

    * **Check `walkaway.threshold`.** The default is `10`. If you lowered this value, visitors may be hitting the threshold faster than expected.
  </Accordion>

  <Accordion title="verifyngo fails to start">
    If verifyngo exits immediately on startup, the most common causes are a config error, a missing required field, or a WHOIS timeout.

    * **JSON syntax error.** Look for a `config:` prefix in the log output — it's followed by the specific parse error and usually includes a line/offset hint. Validate your `config.json` with a JSON linter.

    * **Missing required fields.** verifyngo requires at minimum: `upstream_url`, `cookie_secret`, and a correctly configured provider (with matching `site_key` and `secret_key` set). If any of these are absent or empty, verifyngo will refuse to start.

    * **WHOIS timeout with `whois_strict: true`.** If your `policy_file` contains ASN entries and `asn.whois_strict` is `true`, a WHOIS lookup timeout during startup is treated as a fatal error. Set `"whois_strict": false` to allow verifyngo to start with a warning instead, and investigate the WHOIS connectivity separately.
  </Accordion>
</AccordionGroup>
