...
1# GEP-726: Add Path Redirects and Rewrites
2
3* Issue: [#726](https://github.com/kubernetes-sigs/gateway-api/issues/726)
4* Status: Standard
5
6## TLDR
7
8This GEP proposes adding support for path redirects and rewrites in addition to
9host rewrites. This would augment the existing host redirection capabilities.
10
11## Goals
12
13* Implement path redirects.
14* Implement the most portable and simple forms of path rewrites.
15* Describe how more advanced rewrite and redirect and redirect capabilities
16 could be added in the future.
17
18## API
19
20Although many implementations support very advanced rewrite and redirect
21capabilities, the following are the most [portable](#portability) concepts that
22are not already supported by the Gateway API:
23
24* Path redirects
25* Path prefix redirects
26* Path prefix rewrites
27* Host rewrites
28
29Although regular expression based redirects and rewrites are commonly supported,
30there is significantly more variation in both if and how they are implemented.
31Given the wide support for this concept, it is important to design the API in a
32way that would make it easy to add this capability in the future.
33
34### Path Modifiers
35
36Both redirects and rewrites would share the same `PathModifier` types:
37
38```go
39// HTTPPathModifierType defines the type of path redirect.
40type HTTPPathModifierType string
41
42const (
43 // This type of modifier indicates that the complete path will be replaced by
44 // the path redirect value.
45 AbsoluteHTTPPathModifier HTTPPathModifierType = "Absolute"
46
47 // This type of modifier indicates that any prefix path matches will be
48 // replaced by the substitution value. For example, a path with a prefix match
49 // of "/foo" and a ReplacePrefixMatch substitution of "/bar" will have the
50 // "/foo" prefix replaced with "/bar" in matching requests.
51 PrefixMatchHTTPPathModifier HTTPPathModifierType = "ReplacePrefixMatch"
52)
53
54// HTTPPathModifier defines configuration for path modifiers.
55type HTTPPathModifier struct {
56 // Type defines the type of path modifier.
57 //
58 // +kubebuilder:validation:Enum=Absolute;ReplacePrefixMatch
59 Type HTTPPathModifierType `json:"type"`
60
61 // Substitution defines the HTTP path value to substitute. An empty value ("")
62 // indicates that the portion of the path to be changed should be removed from
63 // the resulting path. For example, a request to "/foo/bar" with a prefix
64 // match of "/foo" would be modified to "/bar".
65 //
66 // +kubebuilder:validation:MaxLength=1024
67 Substitution string `json:"substitution"`
68}
69```
70
71### Redirects
72
73The existing `RequestRedirect` filter can be expanded to support path redirects.
74In the following example, a request to `/foo/abc` would be redirected to
75`/bar/abc`.
76
77```yaml
78kind: HTTPRoute
79apiVersion: gateway.networking.k8s.io/v1alpha2
80metadata:
81 name: http-filter-1
82spec:
83 rules:
84 - matches:
85 - path:
86 type: Prefix
87 value: /foo
88 filters:
89 - type: RequestRedirect
90 requestRedirect:
91 hostname: foo.com
92 path:
93 type: ReplacePrefixMatch
94 value: /bar
95```
96
97This would be represented with the following API addition to the existing
98HTTPRequestRedirect filter:
99```go
100// HTTPRequestRedirect defines a filter that redirects a request. At most one of
101// these filters may be used on a Route rule. This may not be used on the same
102// Route rule as a HTTPRequestRewrite filter.
103//
104// Support: Extended
105type HTTPRequestRedirect struct {
106 // Path defines a path redirect.
107 //
108 // Support: Extended
109 //
110 // +optional
111 Path *HTTPPathModifier `json:"path,omitempty"`
112 // ...
113}
114```
115
116### Rewrites
117
118A new `URLRewrite` filter can be added to support rewrites. In the following
119example, a request to `example.com/foo/abc` would be rewritten to
120`example.net/bar/abc`.
121
122```yaml
123kind: HTTPRoute
124apiVersion: gateway.networking.k8s.io/v1alpha2
125metadata:
126 name: http-filter-1
127spec:
128 hostnames:
129 - example.com
130 rules:
131 - matches:
132 - path:
133 type: Prefix
134 value: /foo
135 filters:
136 - type: URLRewrite
137 requestRewrite:
138 hostname: example.net
139 path:
140 type: ReplacePrefixMatch
141 substitution: /bar
142```
143
144This would be represent with the following API additions:
145```go
146// HTTPURLRewrite defines a filter that modifies a request during forwarding.
147// At most one of these filters may be used on a Route rule. This may not be
148// used on the same Route rule as a HTTPRequestRedirect filter.
149//
150// Support: Extended
151type HTTPURLRewrite struct {
152 // Hostname is the value to be used to replace the Host header value during
153 // forwarding.
154 //
155 // Support: Extended
156 //
157 // +optional
158 // +kubebuilder:validation:MaxLength=255
159 Hostname *string `json:"hostname,omitempty"`
160
161 // Path defines a path rewrite.
162 //
163 // Support: Extended
164 //
165 // +optional
166 Path *HTTPPathModifier `json:"path,omitempty"`
167}
168```
169
170Note: `RequestRewrite` was originally considered as a name for this filter.
171`URLRewrite` was chosen as it more clearly represented the capabilities of the
172filter and would not be confused with header or query param modification.
173
174## Portability
175
176When considering what should be possible in the API, it's worth evaluating what
177common tooling is capable of. This is by no means a complete list, but this
178provides a high level overview of how this is configured across different
179implementations.
180
181Although not all of these implementations directly support prefix rewrites or
182redirects, the ones that don't include regular expression support which can be
183used to implement prefix rewrites and redirects.
184
185Note: This section intentionally excludes the redirect capabilities already
186contained in the API.
187
188### Envoy
189Envoy supports the following relevant capabilities
190([reference](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto)):
191
192* path_redirect (redirect only)
193* prefix_rewrite (redirect and forwarding)
194* regex_rewrite (redirect and forwarding)
195* host_rewrite_literal (forwarding only)
196* strip_query (redirect only)
197
198Note that path rewrite relies on the prefix match for the route, there is not
199a way to differentiate between the prefix used for matching and rewriting.
200
201### Google Cloud
202Google Cloud URL Maps support the following relevant capabilities
203([reference](https://cloud.google.com/compute/docs/reference/rest/v1/urlMaps)):
204
205* pathPrefixRewrite (forwarding only)
206* hostRewrite (forwarding only)
207* pathRedirect (redirect only)
208* prefixRedirect (redirect only)
209* stripQuery (redirect only)
210
211Note that path rewrite relies on the prefix match for the route, there is not
212a way to differentiate between the prefix used for matching and rewriting.
213
214### HAProxy
215HAProxy supports the following relevant capabilities
216([reference](https://cbonte.github.io/haproxy-dconv/2.5/configuration.html)):
217
218* http-request set-path (advanced path rewrite capabilities)
219* http-request replace-path (rewrites entire path)
220* http-request replace-pathq (rewrites entire path + query string)
221* http-request replace-uri (URI rewrite based on input regex)
222* redirect location (advanced redirect capabilities)
223
224### NGINX
225The NGINX rewrite module contains the following relevant capabilities
226([reference](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)):
227
228* PCRE regex based rewrites
229* Rewrite directive can be used during forwarding or redirects
230* Rewrite directive can affect host, path, or both
231* Rewrite directive can be chained
232
233## Future Extension
234There are two relatively common types of path rewrite/redirect that are not
235covered by this proposal:
236
2371. Replace a path prefix separate from the match
2382. Replace with a Regular Expression substitution
239
240Both of the following can be represented by adding a new field new types. For
241example, this config would result in a request to `/foo/baz` to be rewritten to
242`/bar/baz`:
243
244```yaml
245filters:
246- type: RequestRewrite
247 requestRewrite:
248 path:
249 type: ReplacePrefix
250 pattern: /foo
251 substitution: /bar
252```
253
254Similarly, this config would result in a request to `/foo/bar/baz` being
255rewritten to `/foo/other/baz`.
256```yaml
257filters:
258- type: RequestRewrite
259 requestRewrite:
260 path:
261 type: RegularExpression
262 pattern: /foo/(.*)/baz
263 substitution: other
264```
265
266Although both of the above are natural extensions of the API, they are not quite
267as broadly supported. For that reason, this GEP proposes omitting these types
268from the initial implementation.
269
270## Alternatives
271
272### 1. Generic Path Match Replacement
273Instead of the `ReplacePrefixMatch` option proposed above, we could have a
274`ReplacePathMatch` option. This would provide significantly more flexibility and
275room for growth than prefix replacement.
276
277Unfortunately it would be difficult to represent conformance and support levels.
278It also would have limited value. Replacing "Exact" match types would be nearly
279identical to the "Absolute" match type, and replacing "RegularExpression" match
280types would likely not yield the desired result. In most cases, RegEx rewrites
281are implemented separately from RegEx path matching. So a user may want to match
282all paths matching one RegEx, but use a separate RegEx + substitution value for
283rewrites.
284
285It is theoretically possible that future patch match types could be useful as a
286rewrite source, but the common proxies described above seem to be limited to the
287rewrite types described above.
288
289### 2. Top Level Rewrite Fields
290Although a small difference, we could restructure how the path rewrites and
291redirects were configured. One example would be adding top level fields in the
292filters for each kind of path rewrite or redirect. That would result in a change
293like this:
294
295**Before:**
296```yaml
297requestRewrite:
298 hostname: foo.com
299 path:
300 type: Prefix
301 substitution: /bar
302```
303
304**After:**
305```yaml
306requestRewrite:
307 hostname: foo.com
308 pathPrefix: /bar
309```
310
311Although simpler for the initial use cases, it may become more difficult to
312maintain and validate as additional types of rewrites and redirects were added.
313
314
315## References
316
317Issues:
318
319- [#200: Add support for configurable HTTP redirects](https://github.com/kubernetes-sigs/gateway-api/issues/200)
320- [#678: Add support for HTTP rewrites](https://github.com/kubernetes-sigs/gateway-api/issues/678)
View as plain text