symfony/http-foundation · error · BadRequestException

Invalid URI: Host is malformed.

Error message

Invalid URI: Host is malformed.

What it means

Request::create() validates the host component extracted from the URI with self::isHostValid(). If a non-empty host fails that check (disallowed characters or malformed IPv6 literal), the factory refuses to build the Request and throws BadRequestException ('Invalid URI: Host is malformed.'), which maps to an HTTP 400. This blocks host-header injection and DNS-rebinding style URLs at construction time.

Solutions

  1. Fix the URI so its host is a valid registered name (RFC 1123: letters, digits, hyphens, dots) or a bracketed IPv6 literal, e.g. 'http://my-host.example.com/'.
  2. Validate the host before calling: parse_url($uri, PHP_URL_HOST) and run the same kind of check (or filter_var($host, FILTER_VALIDATE_DOMAIN)).
  3. If the value comes from configuration, correct the base-url/host config entry (underscores and spaces are not valid in hostnames).
  4. For untrusted input, catch BadRequestException from Request::create() and reject the request with a 400.

Example fix

// before
$request = Request::create('http://my_host.example.com/path'); // underscore: invalid
// after
$host = parse_url($uri, PHP_URL_HOST);
if (!preg_match('/^(\[?[a-f0-9:.]+\]?)$|^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i', (string) $host)) {
    throw new \InvalidArgumentException('Invalid host in URI');
}
$request = Request::create($uri);
Defensive patterns

Strategy: validation

Validate before calling

function hasValidHost(string $uri): bool
{
    $host = parse_url($uri, PHP_URL_HOST);
    if (null === $host || false === $host || '' === $host) {
        return true; // no host component: this specific check does not apply
    }
    if (preg_match('/^\[.+\]$/', $host)) {
        return false !== filter_var(trim($host, '[]'), FILTER_VALIDATE_IP);
    }

    return (bool) preg_match('/^(?!-)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.?$/', $host);
}
if (!hasValidHost($uri)) {
    throw new \InvalidArgumentException('Invalid host in URI');
}

Type guard

function isWellFormedUrl(string $uri): bool
{
    $c = parse_url($uri);

    return false !== $c && (!isset($c['host']) || '' !== $c['host']);
}

Try / catch

try {
    $request = Request::create($uri);
} catch (BadRequestException $e) {
    if (str_contains($e->getMessage(), 'Host is malformed')) {
        return new Response('Unacceptable host in URL.', 400);
    }

    throw $e;
}

Prevention

When it happens

Trigger: Calling Request::create('http://<bad-host>/...') where the host contains characters outside the allowed set (letters, digits, '-', '.', and bracketed IPv6 per isHostValid), e.g. 'http://host_name/' with underscore, 'http://[not-ipv6]/', or hosts with spaces/commas. parse_url() extracted a 'host' key but isHostValid() rejected it at Request.php:405-406.

Common situations: Building requests from user-provided callback/webhook URLs; misconfigured base URLs in config (typo, underscore in hostname); test suites exercising invalid-host handling; SSRF filters that should reject bad hosts before reaching the framework.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of symfony/http-foundation@5aea19cd67 (2026-09-13). Data as JSON: /api/errors/92eb37115013c2aa. Report an issue: GitHub.

Appendix: source

Thrown at Request.php:406

        ], $server);

        $server['PATH_INFO'] = '';
        $server['REQUEST_METHOD'] = strtoupper($method);

        if (($i = strcspn($uri, ':/?#')) && ':' === ($uri[$i] ?? null) && (strspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.') !== $i || strcspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'))) {
            throw new BadRequestException('Invalid URI: Scheme is malformed.');
        }
        if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {
            throw new BadRequestException('Invalid URI.');
        }

        $part = ($components['user'] ?? '').':'.($components['pass'] ?? '');

        if (':' !== $part && \strlen($part) !== strcspn($part, '[]')) {
            throw new BadRequestException('Invalid URI: Userinfo is malformed.');
        }
        if (($part = $components['host'] ?? '') && !self::isHostValid($part)) {
            throw new BadRequestException('Invalid URI: Host is malformed.');
        }
        if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) {
            throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.');
        }
        if (\strlen($uri) !== strcspn($uri, "\r\n\t")) {
            throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.');
        }
        if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) {
            throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');
        }

        if (isset($components['host'])) {
            $server['SERVER_NAME'] = $components['host'];
            $server['HTTP_HOST'] = $components['host'];
        }

        if (isset($components['scheme'])) {
            if ('https' === $components['scheme']) {

View on GitHub (pinned to 5aea19cd67)