Skip to content

RemoveBodyMiddleware leaves Content-Length on 204 / 1xx responses #29

Description

@Sakanweb

What steps will reproduce the problem?

RemoveBodyMiddleware empties the body for statuses like 204, but does not remove an existing Content-Length header:

private function removeBody(ResponseInterface $response): ResponseInterface
{
    return $response->withBody(
        $this->streamFactory->createStream(),
    );
}

ContentLengthMiddleware also leaves a pre-existing Content-Length alone even on those statuses (shouldSkipContentLength returns early when the header is already set):

return !$this->add
    || $response->hasHeader('Content-Length')
    || in_array($response->getStatusCode(), $this->doNotAddOnStatusCode, true);

Minimal reproduce:

$response = $factory->createResponse(204)
    ->withHeader('Content-Length', '12')
    ->withBody($streamFactory->createStream('hello world!'));

$out = (new RemoveBodyMiddleware($streamFactory))->process($request, fn () => $response);

// body empty, Content-Length: 12 still present

Same problem after a typical pipeline: remove body -> then content-length middleware - existing Content-Length is still not stripped for 204.

What is the expected result?

For statuses where the body must not be sent (defaults include 1xx / 204 / 205 / 304), response must not advertise a non-matching Content-Length. Per RFC 9110 §8.6, a sender MUST NOT generate a Content-Length header field in 1xx (Informational) or 204 (No Content) responses.

After RemoveBodyMiddleware, at least Content-Length (and typically Transfer-Encoding) for those codes should be removed.

What do you get instead?

Empty body with leftover Content-Length: N -> inconsistent message (clients / proxies may mis-frame the connection).

Additional info

Q A
Version yiisoft/http-middleware 1.2.0
PHP version 8.3

Suggested fix

In RemoveBodyMiddleware::removeBody() (and/or ContentLengthMiddleware for statuses in doNotAddOnStatusCode): withoutHeader('Content-Length') (and Transfer-Encoding if present) when body is removed / when status forbids length.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions