AdguardTeam/AdGuardHome · warning

login attempt blocked for %s

Error message

login attempt blocked for %s

What it means

Basic-auth attempt rejected because the source IP is currently blocked by the login rate limiter; the message includes how long the block remains. The deferred handler also counts this failed attempt.

Source

Thrown at internal/home/authhttp.go:583

	r *http.Request,
) (user *aghuser.User, err error) {
	login, pass, ok := r.BasicAuth()
	if !ok {
		return nil, nil
	}

	var remoteIP string
	// The real IP address of the client [realIP] cannot be used here without
	// taking trusted proxies into account due to security issues:
	//
	// See https://github.com/AdguardTeam/AdGuardHome/issues/2799.
	if remoteIP, err = netutil.SplitHost(r.RemoteAddr); err != nil {
		return nil, fmt.Errorf("getting remote address: %w", err)
	}

	rateLimiter := mw.rateLimiter
	if left := rateLimiter.check(remoteIP); left > 0 {
		return nil, fmt.Errorf("login attempt blocked for %s", left)
	}

	defer func() {
		if err != nil {
			rateLimiter.inc(remoteIP)

			return
		}

		rateLimiter.remove(remoteIP)
	}()

	user, _ = mw.users.ByLogin(ctx, aghuser.Login(login))
	if user == nil {
		return nil, errInvalidLogin
	}

	ok = user.Password.Authenticate(ctx, pass)

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Correct the Basic Auth credentials being used
  2. Wait for the block window to expire before retrying
  3. Add exponential backoff / stop-on-401 in automated clients
  4. Use a distinct source IP or trusted-proxy config where applicable

Example fix

// before
resp = basic_auth(user, wrong_pass)
# retry immediately on failure
// after
if resp.status_code == 401 and 'blocked for' in resp.text:
    time.sleep(parse_duration(resp.text))
Defensive patterns

Strategy: retry

Try / catch

// Parse remaining block duration from error and sleep before retry
if strings.Contains(err.Error(), "blocked for") {
    d := parseDurationFromMsg(err.Error())
    time.Sleep(d)
}

Prevention

When it happens

Trigger: HTTP Basic Auth requests from an IP that exceeded failed-login limits; rateLimiter.check(remoteIP) returns a positive remaining duration.

Common situations: Monitoring scripts or API clients using Basic Auth with wrong credentials repeatedly; shared NAT IP accumulating failures; retry loops without backoff.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/d632bad9a2dcbeae. Report an issue: GitHub.