Paul Rütter created FELIX-6849:
----------------------------------
Summary: Jetty 12.1.x uses `DEFAULT_REDIRECT` instead of null, not
honoring the `UriComplianceMode` setting in Felix
Key: FELIX-6849
URL: https://issues.apache.org/jira/browse/FELIX-6849
Project: Felix
Issue Type: Bug
Reporter: Paul Rütter
Since Jetty 12.1, {{HttpConfiguration}} applies URI compliance checks to the
{{Location}} header of HTTP redirects via
{{{}setRedirectUriCompliance(UriCompliance){}}}, separately from the
request-URI check ({{{}setUriCompliance{}}}):
* Jetty 12.0.x: redirect default {{null}} → no check on redirect URIs (Jetty
9.x-like).
* Jetty 12.1.0–12.1.5: redirect default {{UriCompliance.DEFAULT}} → ambiguous
URIs (e.g. encoded {{%2F}} / {{{}AMBIGUOUS_PATH_SEPARATOR{}}}) in the
{{Location}} header are rejected.
* Jetty 12.1.6+
([jetty/jetty.project#14264|https://github.com/jetty/jetty.project/issues/14264]):
redirect default {{UriCompliance.DEFAULT_REDIRECT}} (= {{{}DEFAULT{}}},
additionally tolerating a URI fragment). Still rejects {{{}%2F{}}}.
*Problem*
Felix maps the {{org.eclipse.jetty.UriComplianceMode}} property to
{{HttpConfiguration.setUriCompliance(...)}} only (see
{{{}JettyService.getHttpConfiguration{}}}). It never calls
{{{}setRedirectUriCompliance(...){}}}.
As a result, an application that sets
{{org.eclipse.jetty.UriComplianceMode=LEGACY}} to preserve Jetty 9.x handling
of ambiguous encodings (e.g. {{{}%2F{}}}) gets that behavior for incoming
*request* URIs, but any redirect whose {{Location}} still contains {{%2F}} is
rejected by Jetty's default {{{}DEFAULT_REDIRECT{}}}. There is no way to relax
redirect compliance from Felix configuration, so the request-side relaxation is
effectively undone on redirects.
*Proposal*
Reuse the existing {{org.eclipse.jetty.UriComplianceMode}} property to drive
*both* setters. Differentiating request vs. redirect compliance adds
configuration surface for no real benefit — if a deployment opts into a
compliance mode, it should apply end-to-end.
In {{{}JettyService{}}}, within the existing {{if (uriComplianceMode != null)}}
block, add:
config.setRedirectUriCompliance(compliance);
alongside the current {{{}config.setUriCompliance(compliance){}}}.
Because this stays inside the {{!= null}} guard, behavior is unchanged when the
property is unset: Jetty keeps its own defaults (request {{{}DEFAULT{}}},
redirect {{{}DEFAULT_REDIRECT{}}}). When the property is set to {{LEGACY}} (the
common case for allowing {{{}%2F{}}}), both request and redirect URIs are
relaxed consistently — and {{LEGACY}} is a strict superset of
{{DEFAULT_REDIRECT}} (it also allows {{{}FRAGMENT{}}}), so nothing is lost.
_Minor note:_ for a deployment that explicitly sets the mode to
{{{}DEFAULT{}}}, redirect URIs would become marginally stricter than Jetty's
out-of-box {{DEFAULT_REDIRECT}} (a URI fragment in {{Location}} would no longer
be tolerated). This is an acceptable and arguably expected consequence of
applying a single explicit compliance mode uniformly.
*References*
*
[jetty/jetty.project#14264|https://github.com/jetty/jetty.project/issues/14264]
— introduced {{{}UriCompliance.DEFAULT_REDIRECT{}}}, first released in Jetty
12.1.6 (2026-01-27).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)