Skip to content

Fix GraphQL HTTP responses missing Vary: Accept - #10467

Open
glen-84 wants to merge 3 commits into
mainfrom
gai/vary-accept-on-graphql-responses
Open

glen-84 wants to merge 3 commits into
mainfrom
gai/vary-accept-on-graphql-responses

Conversation

@glen-84

@glen-84 glen-84 commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Every response to a GET, HEAD, POST, or QUERY request on a GraphQL endpoint, other than a WebSocket upgrade, now lists Accept in Vary, whatever its status code and under every transport version. This covers MapGraphQL, MapGraphQLHttp, MapGraphQLPersistedOperations, and the Azure Functions pipeline. The endpoints select the response by Accept (the JSON media type, a single result as SSE or multipart when the client names it, and 406) and copy @cacheControl's Cache-Control onto it, so without Vary a shared or browser cache could hand one client's format to another.
  • A new internal HttpContentNegotiationMiddleware adds the header before any middleware writes a response; the persisted-operation routes get the same middleware through a route-group convention. DefaultHttpResponseFormatter now adds the @cacheControl(vary: …) names beside Accept instead of replacing the header, so Vary values set earlier by application middleware are kept, and a header that already lists * is left as it is. The isolated-process Azure Functions adapter now replaces a response header that is set again, where it used to add a second copy of a multi-valued header and throw for a single-valued one such as Content-Length.
  • The 16.6 to 16.7 migration guide lists the change under behavioral breaking changes, including that some CDNs (Akamai by default) do not cache responses whose Vary lists anything other than Accept-Encoding, with the workarounds. The transport and cache-control pages describe the header and how a custom formatter adds to it. No public API changes.

Test plan

  • HttpContentNegotiationMiddlewareTests and HttpResponseExtensionsTests cover the method and path rules and the merge with existing Vary values.
  • GraphQLOverHttpSpecTests, under Legacy, Draft20250508, and Draft20260903: results over GET, HEAD, POST, and QUERY, a 406, a document syntax error, and a refused GET carry Vary: Accept; OPTIONS, PUT, and a WebSocket upgrade do not. Rows for MapGraphQLHttp and for persisted operation GET, POST, and QUERY.
  • HttpCachingTests: @cacheControl(vary: ["Accept", "X-foo"]) yields Vary: Accept, x-foo with a single Accept.
  • Azure Functions, in-process and isolated: GET and POST results carry Vary: Accept. AzureHeaderDictionaryTests: a header or Content-Length set twice keeps only the second value.
  • Snapshots that record response headers change only by the added Vary: Accept.
  • HotChocolate.AspNetCore.Tests, HotChocolate.Caching.Tests, both Azure Functions test projects, HotChocolate.Fusion.AspNetCore.Tests, HotChocolate.Fusion.Caching.Tests, and HotChocolate.Adapters.OpenApi.Tests pass on net10.0.

Copilot AI lite review requested due to automatic review settings October 2, 2026 14:36
@github-actions github-actions Bot added 📚 documentation This issue is about working on our documentation. 🌶️ hot chocolate labels Oct 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Moderate issues remain with wildcard Vary handling and schema-download negotiation.

Review effort: Lite
Findings: None

What changed in this PR

Adds Vary: Accept handling across GraphQL HTTP transports, persisted operations, Azure Functions, caching, documentation, and regression tests.

Changes:

  • Adds content-negotiation middleware.
  • Merges cache-control Vary values while preserving existing headers.
  • Updates documentation, tests, and response snapshots.

Review notes: changes are still needed for Vary: *, schema-download handling, and WebSocket documentation accuracy.

File Reviewed change
website/​content/​docs/​hotchocolate/​server/​http-transport.md Documents Vary behavior.
website/​content/​docs/​hotchocolate/​server/​cache-control.md Documents cache-control integration.
website/​content/​docs/​hotchocolate/​migrating/​migrate-from-16-6-to-16-7.md Adds migration guidance.
website/​content/​docs/​fusion/​cache-control.md Documents Fusion cache behavior.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​VariableBatchingTests.cs Updates response snapshots.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_Not_Allowed.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_Not_Allowed_Override_Per_Request.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_Not_Allowed_Even_When_Persisted.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_Not_Allowed_Custom_Error.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_By_Default_Works.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.Standard_Query_Allowed_When_Persisted.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.HotChocolateStyle_Sha256Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.HotChocolateStyle_Sha256Hash_Query_Empty_String_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.HotChocolateStyle_Sha1Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.HotChocolateStyle_MD5Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.HotChocolateStyle_MD5Hash_NotFound.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.ApolloStyle_Sha256Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.ApolloStyle_Sha1Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.ApolloStyle_MD5Hash_Success.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​PersistedOperationTests.ApolloStyle_MD5Hash_NotFound.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DefaultSecurityTests.AllowOperationPlanRequests_True_Without_OperationPlanHeader_Should_Omit_OperationPlan.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DefaultSecurityTests.AllowOperationPlanRequests_True_With_OperationPlanHeader_Should_Include_OperationPlan.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DefaultSecurityTests.AllowOperationPlanRequests_False_With_PerRequestOverride_Without_OperationPlanHeader_Should_Omit_OperationPlan.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DefaultSecurityTests.AllowOperationPlanRequests_False_With_PerRequestOverride_And_OperationPlanHeader_Should_Include_OperationPlan.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DefaultSecurityTests.AllowOperationPlanRequests_False_With_OperationPlanHeader_Should_Omit_OperationPlan.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​CostReportingTests.RejectedRequest_Should_ReturnHttp400_When_AcceptIsGraphQLResponseJson.snap Updates expected headers.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​CostReportingTests.RejectedRequest_Should_ReturnHttp200_When_AcceptIsLegacyJson.snap Updates expected headers.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​HttpCachingTests.cs Tests merged Vary values.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.SharedMaxAgeAndVary_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.SharedMaxAgeAndVary_Multiple_Should_Cache_And_Combine.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.SharedMaxAgeAndScope_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.SharedMaxAge_Multiple_Combine_Public_Private_Caches_Private.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.SharedMaxAge_MaxAge_Combine_Produces_Resolved_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.QueryError_Should_Not_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.No_Applied_Defaults_Should_Not_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAgeAndScope_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAge_Zero_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAge_SharedMaxAge_Combine_Produces_Resolved_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAge_NonZero_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAge_Multiple_Should_Cache_Shortest_Time.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.MaxAge_Multiple_Combine_Public_Private_Caches_Private.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.JustScope_Should_Not_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.Just_Defaults_Should_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.Default_Scope_Should_Apply_And_Cache.snap Updates caching snapshot.
src/​HotChocolate/​Caching/​test/​Caching.Tests/​__snapshots__/​HttpCachingTests.Default_Max_Age_Should_Apply_And_Cache.snap Updates caching snapshot.
src/​HotChocolate/​AzureFunctions/​test/​HotChocolate.AzureFunctions.Tests/​InProcessEndToEndTests.cs Tests in-process headers.
src/​HotChocolate/​AzureFunctions/​test/​HotChocolate.AzureFunctions.IsolatedProcess.Tests/​IsolatedProcessEndToEndTests.cs Tests isolated-process headers.
src/​HotChocolate/​AzureFunctions/​src/​HotChocolate.AzureFunctions/​Extensions/​HotChocolateAzureFunctionServiceCollectionExtensions.cs Registers negotiation middleware.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​PersistedOperationMiddlewareTests.cs Tests persisted-operation headers.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​HttpQueryMiddlewareTests.cs Updates QUERY expectations.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​HttpPostMiddlewareTests.cs Updates POST expectations.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​HttpGetSchemaMiddlewareTests.cs Updates schema expectations.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​HttpContentNegotiationMiddlewareTests.cs Tests negotiation rules.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​GraphQLOverHttpSpecTests.cs Tests transport-wide headers.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​Extensions/​HttpResponseExtensionsTests.cs Tests Vary merging.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​__snapshots__/​IntrospectionTests.Introspection_Request_With_Rule_Removed_Fail.md Updates response headers.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​__snapshots__/​IntrospectionTests.Introspection_Request_When_NOT_Development_Fail.md Updates response headers.
src/​HotChocolate/​AspNetCore/​test/​AspNetCore.Tests/​__snapshots__/​IntrospectionTests.Introspection_Request_When_Development_Success.md Updates response headers.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​PersistedOperationMiddleware.cs Applies negotiation to persisted routes.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​MiddlewareFactory.cs Creates negotiation middleware.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​HttpUnsupportedRequestMiddleware.cs Reuses endpoint matching.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​HttpContentNegotiationMiddleware.cs Adds Vary: Accept.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​Formatters/​DefaultHttpResponseFormatter.cs Preserves Vary values.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​Extensions/​HttpResponseExtensions.cs Implements Vary merging.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​Extensions/​HttpRequestExtensions.cs Adds endpoint matching.
src/​HotChocolate/​AspNetCore/​src/​AspNetCore.Pipeline/​Extensions/​EndpointRouteBuilderExtensions.cs Integrates middleware.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The isolated Azure Functions response path may duplicate or reject merged Vary headers.

Review effort: Lite
Findings: None

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The moderate SDL caching issue remains unresolved.

Review effort: Lite
Findings: None

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation This issue is about working on our documentation. 🌶️ hot chocolate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants