crowdsecurity/crowdsec · error

missing '%s' header

Error message

missing '%s' header

What it means

NewParsedRequestFromRequest builds a ParsedRequest from the internal HTTP request the bouncer forwards, which must carry X-Crowdsec-Appsec-IP (IPHeaderName) identifying the original client. This error means that required header is absent, so the client IP is unknown and the request cannot be evaluated.

Source

Thrown at pkg/appsec/request.go:407

	ip := net.ParseIP(host)
	if ip == nil {
		log.Errorf("Invalid appsec remote IP address source %v", remoteAddr)
		return remoteAddr
	}
	return ip.String()
}

// Generate a ParsedRequest from a http.Request. ParsedRequest can be consumed by the App security Engine.
// bodySettings controls the maximum body size and what to do when the limit is exceeded.
func NewParsedRequestFromRequest(r *http.Request, logger *log.Entry, bodySettings BodySettings) (ParsedRequest, error) {
	body, bodyTruncated, bodySizeExceeded, err := readRequestBody(r, bodySettings, logger)
	if err != nil {
		return ParsedRequest{}, err
	}

	clientIP := r.Header.Get(IPHeaderName)
	if clientIP == "" {
		return ParsedRequest{}, fmt.Errorf("missing '%s' header", IPHeaderName)
	}

	clientURI := r.Header.Get(URIHeaderName)
	if clientURI == "" {
		return ParsedRequest{}, fmt.Errorf("missing '%s' header", URIHeaderName)
	}

	clientMethod := r.Header.Get(VerbHeaderName)
	if clientMethod == "" {
		return ParsedRequest{}, fmt.Errorf("missing '%s' header", VerbHeaderName)
	}

	clientHost := r.Header.Get(HostHeaderName)
	if clientHost == "" {
		logger.Debugf("missing '%s' header", HostHeaderName)
	}

	userAgent := r.Header.Get(UserAgentHeaderName)

View on GitHub (pinned to 909b515798)

Solutions

  1. Update/configure your bouncer to send the X-Crowdsec-Appsec-IP header on forwarded requests
  2. Verify no proxy in between strips X-Crowdsec-* headers
  3. Ensure requests reach the appsec endpoint only via the bouncer, not directly
  4. Check bouncer/crowdsec version compatibility (appsec header protocol)

Example fix

// bouncer side: before
req, _ := http.NewRequest("POST", appsecURL, body)
// after
req.Header.Set("X-Crowdsec-Appsec-IP", clientIP)
req.Header.Set("X-Crowdsec-Appsec-URI", uri)
req.Header.Set("X-Crowdsec-Appsec-Verb", method)
req.Header.Set("X-Crowdsec-Appsec-Host", host)
Defensive patterns

Strategy: validation

Validate before calling

if r.Header.Get("X-Crowdsec-Appsec-IP") == "" {
    // don't call NewParsedRequestFromRequest; reject or skip appsec
}

Try / catch

pr, _, _, err := NewParsedRequestFromRequest(r, logger)
if err != nil {
    log.Errorf("malformed appsec request: %v", err)
    http.Error(w, "bad request", http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: appsecHandler (or tests) calls NewParsedRequestFromRequest on a request whose IPHeaderName header is missing or empty — usually a misconfigured bouncer or a direct request hitting the appsec endpoint bypassing the bouncer.

Common situations: Bouncer not configured to forward X-Crowdsec-Appsec-* headers, older bouncer version predating the header set, direct curl/browser access to the appsec port, or a proxy stripping custom headers.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/7420c289f8b2f769. Report an issue: GitHub.