Skip to main content
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.
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.
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:
  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.
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.
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.
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).
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.
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.