pinpoint-apm/pinpoint · error · IllegalArgumentException
Webhook URL host is not allowed
Error message
Webhook URL host is not allowed
What it means
WebhookUrlValidator.validateAuthority throws IllegalArgumentException when the URL host is a blocked host literal (isBlockedHostLiteral), e.g. localhost, loopback, link-local, or private IP literals. This is an SSRF protection: webhooks must not target internal infrastructure.
Source
Thrown at commons-server/src/main/java/com/navercorp/pinpoint/common/server/webhook/WebhookUrlValidator.java:123
private static void validateScheme(URI uri) {
String scheme = uri.getScheme();
if (scheme == null) {
throw new IllegalArgumentException("Webhook URL scheme is required");
}
String normalizedScheme = scheme.toLowerCase(Locale.ROOT);
if (!"http".equals(normalizedScheme) && !"https".equals(normalizedScheme)) {
throw new IllegalArgumentException("Webhook URL scheme must be http or https");
}
}
private static void validateAuthority(URI uri) {
if (uri.getHost() == null || uri.getHost().isBlank()) {
throw new IllegalArgumentException("Webhook URL host is required");
}
if (isBlockedHostLiteral(uri.getHost())) {
throw new IllegalArgumentException("Webhook URL host is not allowed");
}
if (uri.getRawUserInfo() != null) {
throw new IllegalArgumentException("Webhook URL user info is not allowed");
}
if (uri.getRawFragment() != null) {
throw new IllegalArgumentException("Webhook URL fragment is not allowed");
}
int port = uri.getPort();
if (port == -1 && hasExplicitPort(uri)) {
throw new IllegalArgumentException("Webhook URL port is not valid");
}
if (port == 0 || port > MAX_PORT) {
throw new IllegalArgumentException("Webhook URL port is not allowed");
}
}
private static boolean hasExplicitPort(URI uri) {
String rawAuthority = uri.getRawAuthority();View on GitHub (pinned to 744c3d3075)
Solutions
- Use a publicly reachable host name / IP for the webhook target
- Expose the local test receiver via a tunnel (e.g. ngrok) and use its public URL
- If an internal host is legitimately required, extend isBlockedHostLiteral's allowlist consciously (understand the SSRF risk)
Example fix
// before
validator.validateSyntax("http://localhost:8080/webhook");
// after
validator.validateSyntax("https://hooks.example.com/webhook"); Defensive patterns
Strategy: validation
Validate before calling
java.net.InetAddress a = java.net.InetAddress.getByName(java.net.URI.create(url).getHost()); if (a.isLoopbackAddress() || a.isLinkLocalAddress() || a.isSiteLocalAddress()) reject(url);
Type guard
boolean isPublicHost(java.net.URI uri) { try { java.net.InetAddress a = java.net.InetAddress.getByName(uri.getHost()); return !a.isLoopbackAddress() && !a.isLinkLocalAddress() && !a.isSiteLocalAddress(); } catch (Exception e) { return false; } } Try / catch
try { WebhookUrlValidator.validateSyntax(url); } catch (IllegalArgumentException e) { if (e.getMessage().contains("not allowed")) { reject internal host (SSRF guard); } } Prevention
- Use publicly reachable endpoints for webhooks; tunnel local dev receivers via ngrok
- Never target 169.254.169.254 or RFC1918 ranges
- Do not weaken the blocked-literal list without an SSRF risk review
When it happens
Trigger: Configuring webhook URLs like 'http://127.0.0.1/hook', 'http://localhost:8080', 'http://169.254.169.254/', 'http://10.0.0.5/hook', 'http://[::1]/' to validateSyntax/uri.
Common situations: Developer points webhook at a local test server; someone tries to reach cloud metadata endpoints (169.254.169.254); internal service URLs mistakenly used as public webhook targets.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Webhook URL scheme must be http or https
- Webhook URL user info is not allowed
- Webhook URL resolves to a non-public address
- Webhook URL resolves to a private address that is not allowe
- Webhook URL is required
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/1502f1b3990d3e30.
Report an issue: GitHub.