fix(state): omit Content-Type when response has no body#8218
Merged
soyuka merged 1 commit intoJun 3, 2026
Conversation
f4ffe0d to
dcaa4eb
Compare
Per RFC 7230 §3.3.2, a sender MUST NOT generate a Content-Type field in a message containing no payload body, and per §3.3.3, 204, 205 and 304 responses MUST NOT include a body. HttpResponseHeadersTrait::getHeaders() now gates Content-Type behind a $hasBody check that omits Content-Type when the operation explicitly disables output (output: ['class' => null]) or when the resolved status is 204, 205 or 304. Operations without configured output (e.g. the synthetic Get built by JsonLd ContextAction) keep their Content-Type. Fixes api-platform#2929
dcaa4eb to
f3a64b5
Compare
dunglas
approved these changes
Jun 2, 2026
| ]; | ||
|
|
||
| if ($hasBody) { | ||
| $headers['Content-Type'] = \sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())); |
Member
There was a problem hiding this comment.
Unrelated, but the charset attribute unconditionally may not be a good idea. For instance JSON, doesn't have a charset attribute and including it is not valid, and can break some strict clients.
Member
Author
There was a problem hiding this comment.
Indeed this is to avoid breaking tests we had that before. Lets investigate this in another issue.
4 tasks
soyuka
added a commit
to soyuka/core
that referenced
this pull request
Jun 3, 2026
…e it JSON-based media types (RFC 8259, RFC 6839 `+json` suffix) do not define a `charset` parameter, and including one can break strict clients. Restrict `; charset=utf-8` to `text/*`, `application/xml` and `application/xml-external-parsed-entity`, matching their IANA registrations. Updates functional tests asserting on Content-Type accordingly. Refs api-platform#8218
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2929.
Summary
Per RFC 7230 §3.3.2: "A sender MUST NOT generate a Content-Type header field in a message that does not contain a payload body."
Per RFC 7230 §3.3.3: responses with status
204 No Content,205 Reset Content, and304 Not ModifiedMUST NOT include a payload body.HttpResponseHeadersTrait::getHeaders()was unconditionally settingContent-Typeeven when the operation produced no body (e.g.output: false) or when the status code mandates an empty body (DELETE → 204).Change
Gate the
Content-Typeheader behind:Where
$hasOutputisfalsewhenoutput['class'] === null(i.e.output: falsewas passed to the operation).Tests
Three new unit tests in
RespondProcessorTest:testDoesNotSetContentTypeWhenOutputIsFalse— POST withoutput: false+ 204testDoesNotSetContentTypeWhenOutputIsFalseWithCreatedStatus— POST withoutput: false+ 201testDoesNotSetContentTypeOnBodylessStatusCodes— DELETE (default 204)Note on Symfony
Response::prepare()Symfony's
Response::prepare()automatically re-adds aContent-Typeheader derived from the request format for all responses whose status is not204or304(seeResponse::isEmpty()). For the typicaloutput: falseflow (status defaults to204viaInputOutputResourceMetadataCollectionFactory), Symfony's strip aligns with this fix and the header is fully omitted on the wire.For the edge case where a user sets
output: falsetogether with a status that Symfony does not treat as empty (e.g.201,202), Symfony will re-addContent-Type. A follow-up tied to #7875 (which introducesResponseHeaderParameterfor declarative header customization) can add akernel.responselistener that honours the no-body intent regardless of status.