symfony/http-foundation · error · BadRequestException

Invalid URI: A URI must not start nor end with ASCII…

Error message

Invalid URI: A URI must not start nor end with ASCII control characters or spaces.

What it means

Request::create() requires the URI to neither start nor end with an ASCII character <= 32 (control characters or spaces). Leading/trailing whitespace or control bytes usually indicate unsanitized input and can be used to smuggle alternate request lines; the library throws BadRequestException ('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.') producing HTTP 400.

Solutions

  1. Trim the URI before passing it: Request::create(trim($uri)).
  2. Validate up front: reject if $uri === trim($uri, " \t\n\r\0\x0B") is false, or if ord($uri[0]) <= 32.
  3. Fix the source that appends/prepends whitespace (e.g. rtrim() file/DB values, remove quotes from CLI input).
  4. Catch BadRequestException when the URI is untrusted and return a 400 to the client.

Example fix

// before
$request = Request::create(' http://example.com/path ');
// after
$uri = trim(' http://example.com/path ');
if ('' !== $uri && (ord($uri[0]) <= 32 || ord($uri[-1]) <= 32)) {
    throw new \InvalidArgumentException('URI has leading/trailing control chars');
}
$request = Request::create($uri);
Defensive patterns

Strategy: validation

Validate before calling

function hasCleanEnds(string $uri): bool
{
    return '' === $uri || (ord($uri[0]) > 32 && ord($uri[-1]) > 32);
}
$uri = trim($uri);
if (!hasCleanEnds($uri)) {
    throw new \InvalidArgumentException('URI has leading/trailing control chars');
}
$request = Request::create($uri);

Type guard

function isTrimmedUri(mixed $uri): bool
{
    return \is_string($uri) && $uri === trim($uri, " \t\n\r\0\x0B") && '' !== $uri;
}

Try / catch

try {
    $request = Request::create($uri);
} catch (BadRequestException $e) {
    if (str_contains($e->getMessage(), 'control characters or spaces')) {
        $request = Request::create(trim($uri));
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling Request::create() with a URI that begins or ends with a space, NUL, or other byte with ord() <= 32, e.g. ' http://host/' or "http://host/\0" — checked at Request.php:413-415 via ord($uri[0]) <= 32 || ord($uri[-1]) <= 32 on a non-empty URI. Empty-string URIs are exempt from this specific check.

Common situations: URIs pasted with surrounding whitespace from logs, emails, or CLI arguments; values read from files/DB with trailing newline (handled by the previous check) or trailing spaces; fuzzing/security tests prepending NUL bytes or spaces to bypass filters.

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/86653833895277a0. Report an issue: GitHub.

Appendix: source

Thrown at Request.php:415

            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']) {
                $server['HTTPS'] = 'on';
                $server['SERVER_PORT'] = 443;
            } else {
                unset($server['HTTPS']);
                $server['SERVER_PORT'] = 80;
            }
        }

        if (isset($components['port'])) {

View on GitHub (pinned to 5aea19cd67)