symfony/http-foundation · error · InvalidArgumentException

The filename and the fallback cannot contain the "/" and…

Error message

The filename and the fallback cannot contain the "/" and "\" characters.

What it means

Path separators '/' and '\' are forbidden in both $filename and $filenameFallback to prevent Content-Disposition header values from carrying path components, which would enable header injection / path-traversal style attacks against clients that honor directory parts in the filename. makeDisposition() throws this InvalidArgumentException if either string contains either separator.

Solutions

  1. Pass only the basename: HeaderUtils::makeDisposition($d, basename($path))
  2. Strip separators from user input: str_replace(['/', '\\'], '', $filename) or preg_replace('#[/\\\\]#', '_', $name)
  3. Reject requests containing path separators before building the header (defense against traversal)
  4. Wrap in try/catch InvalidArgumentException, sanitize, and retry with the cleaned name

Example fix

// before
HeaderUtils::makeDisposition('attachment', '/var/www/uploads/report.pdf'); // throws
// after
HeaderUtils::makeDisposition('attachment', basename('/var/www/uploads/report.pdf')); // 'report.pdf'
Defensive patterns

Strategy: validation

Validate before calling

$name = basename(str_replace('\\', '/', $filename));
if ($name !== $filename) {
    throw new \InvalidArgumentException('Filename must not contain path separators.');
}

Type guard

function hasNoPathSeparators(string $s): bool {
    return !str_contains($s, '/') && !str_contains($s, '\\');
}

Try / catch

try {
    $header = HeaderUtils::makeDisposition($disposition, $filename);
} catch (\InvalidArgumentException $e) {
    $header = HeaderUtils::makeDisposition($disposition, basename(str_replace('\\', '/', $filename)));
}

Prevention

When it happens

Trigger: Calling makeDisposition($disposition, $filename) or with a fallback where $filename or $filenameFallback contains '/' or '\' — e.g. 'uploads/report.pdf', 'C:\\temp\\a.pdf', or a user-supplied '../../etc/passwd' filename.

Common situations: Passing a full server-side file path instead of just the basename; echoing unsanitized user upload names (path traversal attempt) into the Content-Disposition header; Windows paths with backslashes.

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


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

Appendix: source

Thrown at HeaderUtils.php:187

        }

        if ('' === $filenameFallback) {
            $filenameFallback = $filename;
        }

        // filenameFallback is not ASCII.
        if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
            throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
        }

        // percent characters aren't safe in fallback.
        if (str_contains($filenameFallback, '%')) {
            throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
        }

        // path separators aren't allowed in either.
        if (str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\')) {
            throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
        }

        $params = ['filename' => $filenameFallback];
        if ($filename !== $filenameFallback) {
            $params['filename*'] = "utf-8''".rawurlencode($filename);
        }

        return $disposition.'; '.self::toString($params, ';');
    }

    /**
     * Like parse_str(), but preserves dots in variable names.
     */
    public static function parseQuery(string $query, bool $ignoreBrackets = false, string $separator = '&'): array
    {
        $q = [];

        foreach (explode($separator, $query) as $v) {

View on GitHub (pinned to 5aea19cd67)