diff --git a/src/http-gateways/path-gateway.md b/src/http-gateways/path-gateway.md index c207ae6d..8f9e12cb 100644 --- a/src/http-gateways/path-gateway.md +++ b/src/http-gateways/path-gateway.md @@ -256,7 +256,7 @@ These are the equivalents: - `format=ipns-record` → `Accept: application/vnd.ipfs.ipns-record` When both `Accept` HTTP header and `format` query parameter are present, -`Accept` SHOULD take precedence. +`format` SHOULD take precedence. :::note diff --git a/src/http-gateways/trustless-gateway.md b/src/http-gateways/trustless-gateway.md index e892d915..a8bd5a0f 100644 --- a/src/http-gateways/trustless-gateway.md +++ b/src/http-gateways/trustless-gateway.md @@ -128,17 +128,15 @@ Same as [`format`](https://specs.ipfs.tech/http-gateways/path-gateway/#format-re - `format=car` → `application/vnd.ipld.car` - `format=ipns-record` → `application/vnd.ipfs.ipns-record` +When both `Accept` HTTP header and `format` query parameter are present, +`format` SHOULD take precedence. + :::note A Client SHOULD include the `format` query parameter in the request URL, in addition to the `Accept` header. This provides the best interoperability and ensures consistent HTTP cache behavior across various gateway implementations. -When both the `Accept` header and `format` parameter are present, a specific -`Accept` value (e.g., `application/vnd.ipld.raw`) SHOULD take precedence over -`format`. Wildcards (e.g., `*/*`, `application/*`) are not specific and do not -take precedence (as specified in :cite[path-gateway]). - ::: ### :dfn[`dag-scope`] (request query parameter) @@ -239,7 +237,7 @@ Optional, only used on CAR requests. Serves same purpose as [CAR `version` content type parameter](#car-version-content-type-parameter). -In case both are present in the request, the value from the [`Accept`](#accept-request-header) HTTP Header has priority and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. +In case both are present in the request, the URL query parameter SHOULD take precedence and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. ### :dfn[`car-order`] (request query parameter) @@ -247,7 +245,7 @@ Optional, only used on CAR requests. Serves same purpose as [CAR `order` content type parameter](#car-order-content-type-parameter). -In case both are present in the request, the value from the [`Accept`](#accept-request-header) HTTP Header has priority and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. +In case both are present in the request, the URL query parameter SHOULD take precedence and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. ### :dfn[`car-dups`] (request query parameter) @@ -255,7 +253,7 @@ Optional, only used on CAR requests. Serves same purpose as [CAR `dups` content type parameter](#car-dups-content-type-parameter). -In case both are present in the request, the value from the [`Accept`](#accept-request-header) HTTP Header has priority and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. +In case both are present in the request, the URL query parameter SHOULD take precedence and a matching [`Content-Location`](#content-location-response-header) SHOULD be returned with the response. # HTTP Response diff --git a/src/ipips/ipip-0523.md b/src/ipips/ipip-0523.md new file mode 100644 index 00000000..5092df9d --- /dev/null +++ b/src/ipips/ipip-0523.md @@ -0,0 +1,132 @@ +--- +title: "IPIP-0523: Prefer format param over Accept header" +date: 2025-11-26 +ipip: proposal +editors: + - name: Alex Potsides + github: achingbrain + url: https://achingbrain.net + affiliation: + name: Shipyard + url: https://ipshipyard.com + - name: Marcin Rataj + github: lidel + affiliation: + name: Shipyard + url: https://ipshipyard.com +relatedIssues: + - https://github.com/ipfs/specs/issues/521 +order: 523 +tags: ['ipips'] +--- + +## Summary + +Prefer the `?format=` URL query parameter over the `Accept:` HTTP request header. + +## Motivation + +The [`Accept`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept) +HTTP request header can be sent with an HTTP request to provide a prioritized +list of response formats the client will accept for a given resource. + +The [`format`](https://specs.ipfs.tech/http-gateways/path-gateway/#format-request-query-parameter) +URL query parameter can also be sent to an IPFS HTTP Gateway to provide the +same information, and is typically done when sending an HTTP header is difficult +or impractical, for example when using a browser address bar. + +The existing [Path Gateway](https://specs.ipfs.tech/http-gateways/path-gateway/#format-request-query-parameter) and [Trustless Gateway](https://specs.ipfs.tech/http-gateways/trustless-gateway/#format-request-query-parameter) specs say: + +> When both the Accept header and format parameter are present, a specific Accept value (e.g., application/vnd.ipld.raw) SHOULD take precedence over format. + +This makes it impossible for browsers to use the `format` URL query parameter, +since they will always send an `Accept` HTTP header which would then cause +the `format` URL query parameter to be ignored. + +## Detailed design + +The priority of the `format` URL query parameter vs the `Accept` HTTP header is +reversed in both :cite[path-gateway] and :cite[trustless-gateway] specs: the +`format` URL query parameter SHOULD always take precedence over the `Accept` +HTTP header when both are present. + +This simplifies the specification by removing the previous wildcard exception +logic. Implementations no longer need to distinguish between specific `Accept` +header values (e.g., `application/vnd.ipld.raw`) and wildcards (e.g., `*/*`) +when determining precedence. + +## Design rationale + +Browsers will always send an `Accept` HTTP header that contains specific values +so it cannot be allowed to take priority over the `format` URL query parameter. + +### User benefit + +Users will be able to use the `format` URL query parameter to control the +response type of requests made from browser address bars. + +### Compatibility + +This change simplifies precedence rules by making the `format` URL query +parameter always take priority over the `Accept` HTTP header when both are +present. + +In practice, this is largely compatible with existing web browser use cases. +Browsers send `Accept` HTTP headers with wildcards (e.g., `*/*` or +`text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`), and +the previous spec already treated wildcards as non-specific, allowing `format` +to take precedence. This means browser address bar usage with `?format=` was +already working as expected. + +In recent years we also realized that HTTP cache implementations are often +flawed, and virtually all HTTP clients add explicit `?format=` anyway to ensure +a unique HTTP cache key is used for each URL. This provides extra protection +from poorly written or configured software and CDNs that comingle different +response types under the same cache key (e.g., deserialized response, raw block, +and CAR being cached and returned based on what was requested and cached first). +By prioritizing `?format=` we ensure deterministic HTTP caching behavior across +the ecosystem, making it easier to deploy and reason about HTTP trustless +gateways. + +The actual breakage is limited to edge cases where a client sends both a +specific `Accept` HTTP header (e.g., `Accept: application/vnd.ipld.raw`) and a +different `format` URL query parameter (e.g., `?format=car`). Previously, the +specific `Accept` header value would win; now `format` wins. This scenario is +rare in practice and arguably represents a client misconfiguration. + +The primary impact is on [gateway-conformance](https://github.com/ipfs/gateway-conformance/) +tests, which explicitly test the old precedence behavior. A minor version bump +of gateway-conformance is required to update these tests. + +### Security + +This change has no security implications. It only affects which response format +is returned when a client sends conflicting format preferences, and does not +change authentication, authorization, or data integrity behaviors. + +### Alternatives + +**Keep status quo with wildcard exception**: The previous spec already had a +carve-out where wildcards (e.g., `*/*`, `application/*`) in the `Accept` HTTP +header did not take precedence over the `format` URL query parameter. This meant +browser use cases were effectively supported, since browsers include wildcards +in their default `Accept` HTTP headers. + +This alternative was rejected because: + +1. The wildcard exception adds implementation complexity +2. The resulting behavior is harder to reason about and document +3. The simpler rule ("format always wins") is easier to understand and implement +4. Real-world browser use cases work identically under both rules + +## Test fixtures + +Implementers can either write own test that prefers the `format` URL query +parameter over any present `Accept` HTTP header, or run the +[gateway-conformance](https://github.com/ipfs/gateway-conformance/) test suite, +which includes tests for this scenario since +[gateway-conformance/pull/252](https://github.com/ipfs/gateway-conformance/pull/252). + +### Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).