unclecode/crawl4ai · warning · ValueError

webhook header not allowed: {name}

Error message

webhook header not allowed: {name}

What it means

ValueError from sanitize_webhook_headers when a header name (case-insensitive) is in _WEBHOOK_DENY_HEADERS - hop-by-hop and security-sensitive headers (content-length, host, connection, authorization, cookie, transfer-encoding, etc.) that users must not override on webhook deliveries.

Source

Thrown at deploy/docker/webhook.py:74

    "content-type", "proxy-authorization", "authorization", "cookie",
    "expect", "upgrade", "te", "trailer",
}
_MAX_WEBHOOK_HEADERS = 20
_MAX_WEBHOOK_HEADER_VALUE = 2048


def sanitize_webhook_headers(headers: Optional[Dict[str, str]]) -> Dict[str, str]:
    """Validate user-supplied webhook headers; raise ValueError on any bad one."""
    if not headers:
        return {}
    if len(headers) > _MAX_WEBHOOK_HEADERS:
        raise ValueError("too many webhook headers")
    clean: Dict[str, str] = {}
    for name, value in headers.items():
        if not isinstance(name, str) or not _WEBHOOK_HEADER_NAME.match(name):
            raise ValueError(f"invalid webhook header name: {name!r}")
        if name.lower() in _WEBHOOK_DENY_HEADERS:
            raise ValueError(f"webhook header not allowed: {name}")
        sval = str(value)
        if len(sval) > _MAX_WEBHOOK_HEADER_VALUE or any(c in sval for c in "\r\n\x00"):
            raise ValueError(f"invalid value for webhook header {name}")
        clean[name] = sval
    return clean


class WebhookDeliveryService:
    """Handles webhook delivery with exponential backoff retry logic."""

    def __init__(self, config: Dict):
        """
        Initialize the webhook delivery service.

        Args:
            config: Application configuration dictionary containing webhook settings
        """
        self.config = config.get("webhooks", {})

View on GitHub (pinned to 7e80152142)

Solutions

  1. Remove the deny-listed header; let the server generate transport headers itself
  2. If you need auth on the webhook, use a custom header name like X-Webhook-Token - check _WEBHOOK_DENY_HEADERS in deploy/docker/webhook.py for your version
  3. Pass auth material as a query param or in the payload only if the receiver's threat model allows

Example fix

# before
"headers": {
  "Host": "example.com",
  "Authorization": "Bearer x",
  "Content-Length": "123"
}

# after
"headers": {
  "X-Webhook-Token": "x"
}
Defensive patterns

Strategy: validation

Validate before calling

DENY = {"content-length","host","connection","proxy-authorization","authorization","cookie","expect","upgrade","te","trailer"}
headers = {k: v for k, v in headers.items() if k.lower() not in DENY}

Type guard

def has_no_denied_headers(h: dict) -> bool:
    DENY = {"content-length","host","connection","proxy-authorization","authorization","cookie","expect","upgrade","te","trailer"}
    return not any(k.lower() in DENY for k in h)

Try / catch

try:
    sanitize_webhook_headers(headers)
except ValueError as e:
    if "not allowed" in str(e):
        bad = next(k for k in headers if k.lower() in DENY)
        headers.pop(bad)
        sanitize_webhook_headers(headers)

Prevention

When it happens

Trigger: Including 'Content-Length', 'Host', 'Connection', 'Authorization', 'Cookie', 'Transfer-Encoding', 'Upgrade', 'Expect', 'TE', 'Trailer', or a proxy-authorization header in webhook.headers.

Common situations: Copying a full curl -H set or browser devtools request headers into the webhook config; trying to authenticate the webhook with an Authorization header not realizing it is deny-listed by the current server version.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/41135419ba95c0ba. Report an issue: GitHub.