Skip to main content
The Custom responder allows you to return a personalized message with any HTTP status code, giving you complete control over the response sent to blocked clients.

Overview

This responder is highly flexible, allowing you to craft specific responses for different scenarios. Whether you want to return a friendly message with 200 OK, pretend the resource doesn’t exist with 404, or indicate legal restrictions with 451, the Custom responder handles it all.

Configuration

message
string
required
The custom text message to return to blocked clients.This field is required when using the Custom responder.
status_code
number
The HTTP status code to return.Can be any valid HTTP status code (e.g., 200, 403, 404, 451, 503).Default: 200
ranges
string[]
IP ranges to target. Can be CIDR notations or predefined service keys.Default: ["aws", "azurepubliccloud", "deepseek", "gcloud", "githubcopilot", "openai"]
whitelist
string[]
Optional list of specific IP addresses to exclude from the custom response.Default: []

HTTP Response

The Custom responder sets:
status
number
The configured status_code (default: 200)
body
string
The configured message text
Content-Type
string
text/plain

Examples

localhost:8080 {
    defender custom {
        ranges openai aws
        message "Please contact support for API access"
    }
    respond "Public content"
}

Implementation Details

The Custom responder is implemented in responders/custom.go:20:
func (c CustomResponder) ServeHTTP(w http.ResponseWriter, _ *http.Request, _ caddyhttp.Handler) error {
    // Use default status code if not specified
    statusCode := c.StatusCode
    if statusCode == 0 {
        statusCode = http.StatusOK
    }

    w.Header().Set("Content-Type", "text/plain")
    w.WriteHeader(statusCode)
    _, err := w.Write([]byte(c.Message))
    return err
}

Common Status Codes

CodeNameUse Case
200OKPolite message without indicating an error
403ForbiddenExplicit access denial
404Not FoundStealth mode - hide that the resource exists
451Unavailable For Legal ReasonsGeographic or legal restrictions
503Service UnavailableMaintenance or temporary blocking

Use Cases

Polite Blocking (200 OK)

Return a friendly message without triggering error handling in client applications:
defender custom {
    ranges openai
    message "We appreciate your interest! Please visit our API page for official access."
}

Stealth Mode (404 Not Found)

Make scrapers think the content doesn’t exist:
defender custom {
    ranges scrapers
    message "Not Found"
    status_code 404
}
Indicate content is unavailable due to legal reasons:
defender custom {
    ranges eu-region
    message "Content unavailable in your jurisdiction"
    status_code 451
}

Maintenance Mode (503)

Temporarily block all traffic during maintenance:
defender custom {
    ranges all
    message "Scheduled maintenance in progress. Back soon!"
    status_code 503
}

Comparison with Other Responders

  • vs Block: Custom allows any status code and message, Block is fixed at 403
  • vs Drop: Custom sends a proper HTTP response, Drop terminates the connection
  • vs Redirect: Custom returns content, Redirect sends clients elsewhere
  • vs Garbage: Custom returns meaningful text, Garbage returns random data

Best Practices

  1. Use 200 for soft blocking - Avoids triggering retry logic in well-behaved bots
  2. Use 404 for stealth - Makes scrapers think content doesn’t exist
  3. Use 403 for explicit denial - Clear signal that access is intentionally blocked
  4. Keep messages short - Reduces bandwidth for blocked requests
  5. Match status to intent - Use semantically appropriate HTTP status codes

Build docs developers (and LLMs) love