Change:
<!-- This form is for bug reports and feature requests ONLY! If you're looking for help check and the . --Is this a BUG REPORT or FEATURE REQUEST?: What happened: When accessing a service (e.g., ) by using the Apiserver proxy feature and the service returns with header, such as The returned header does not preserve the path if the service sent a redirect with an absolute path. Depending on the cluster, the header written by the service itself might have been and the apiserver proxy did rewrite the host:port to match the correct values. Unfortunately, it did not rewrite the path segment. What you expected to happen: I would expect the apiserver to rewrite the path segment of the response header so that it reflects the fact that it's proxied under the path: How to reproduce it (as minimally and precisely as possible): The path was not preserved. Anything else we need to know?: To work around this issue, a service may return strictly a relative url in the header such as: The feature to rewrite headers is quite common. For instance, Apache Httpd does it thanks to: Environment: Kubernetes version (use ):Cloud provider or hardware configuration**: custom_ OS (e.g. from /etc/os-release): Kernel (e.g. ):
/sig api-machinery /sig network
/assign
Thanks for the bug report!
GitHub didn't allow me to assign the following users: roycaihw. Note that only can be assigned. <detailsIn response to : /assign Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details

Discussion:
} if redirect := resp.Header.Get("Location"); redirect != "" { <del> resp.Header.Set("Location", t.rewriteURL(redirect, req.URL)) </del> <ins> resp.Header.Set("Location", t.rewriteURL(redirect, req.URL, req.Host)) </ins> return resp, nil }

// rewriteURL rewrites a single URL to go through the proxy, if the URL refers // to the same host as sourceURL, which is the page on which the target URL <del> // occurred. If any error occurs (e.g. parsing), it returns targetURL. func (t *Transport) rewriteURL(targetURL string, sourceURL *url.URL) string { </del> <ins> // occurred, or if the URL matches the sourceRequestHost. If any error occurs (e.g. // parsing), it returns targetURL. func (t *Transport) rewriteURL(targetURL string, sourceURL *url.URL, sourceRequestHost string) string { </ins> url, err := url.Parse(targetURL) if err != nil { return targetURL } <del> isDifferentHost := url.Host != "" && url.Host != sourceURL.Host </del> <ins> // Example: //      When API server processes a proxy request to a service (e.g. /api/v1/namespace/foo/service/bar/proxy/), //      the sourceURL.Host (i.e. req.URL.Host) is the endpoint IP address of the service. The //      sourceRequestHost (i.e. req.Host) is the Host header that specifies the host on which the //      URL is sought, which can be different from sourceURL.Host. For example, if user sends the //      request through "kubectl proxy" locally (i.e. localhost:8001/api/v1/namespace/foo/service/bar/proxy/), //      sourceRequestHost is "localhost:8001". // //      If the service's response URL contains non-empty host, and url.Host is equal to either sourceURL.Host //      or sourceRequestHost, we should not consider the returned URL to be a completely different host. //      It's the API server's responsibility to rewrite a same-host-and-absolute-path URL and append the //      necessary URL prefix (i.e. /api/v1/namespace/foo/service/bar/proxy/). isDifferentHost := url.Host != "" && url.Host != sourceURL.Host && url.Host != sourceRequestHost </ins> isRelative := !strings.HasPrefix(url.Path, "/") if isDifferentHost || isRelative { return targetURL } <del> url.Scheme = t.Scheme url.Host = t.Host </del> <ins> // Do not rewrite scheme and host if the Transport has empty scheme and host // when targetURL already contains the sourceRequestHost if !(url.Host == sourceRequestHost && t.Scheme == "" && t.Host == "") { url.Scheme = t.Scheme url.Host = t.Host } </ins> origPath := url.Path // Do not rewrite URL if the sourceURL already contains the necessary prefix. if strings.HasPrefix(url.Path, t.PathPrepend) {

} urlRewriter := func(targetUrl string) string { <del> return t.rewriteURL(targetUrl, req.URL) </del> <ins> return t.rewriteURL(targetUrl, req.URL, req.Host) </ins> } err := rewriteHTML(reader, writer, urlRewriter) if err != nil {

Host:        "foo.com", PathPrepend: "/proxy/node/node1:10250", } <ins> emptyHostAndSchemeTransport := &Transport{ PathPrepend: "/proxy/node/node1:10250", } </ins> type Item struct { input        string sourceURL    string

forwardedURI string redirect     string redirectWant string <ins> reqHost      string </ins> } table := map[string]Item{

redirectWant: "http://example.com/redirected/target/", forwardedURI: "/proxy/node/node1:10250/redirect", }, <ins> "redirect abs use reqHost no host no scheme": { sourceURL:    "http://mynode.com/redirect", transport:    emptyHostAndSchemeTransport, redirect:     "http://10.0.0.1:8001/redirected/target/", redirectWant: "http://10.0.0.1:8001/proxy/node/node1:10250/redirected/target/", forwardedURI: "/proxy/node/node1:10250/redirect", reqHost:      "10.0.0.1:8001", }, </ins> "source contains the redirect already": { input:        `<pre><a href="kubelet.log">kubelet.log</a><a href="http://foo.com/proxy/node/node1:10250/google.log">google.log</a></pre>`, sourceURL:    "http://foo.com/logs/log.log",

t.Errorf("%v: Unexpected error: %v", name, err) return } <ins> if item.reqHost != "" { req.Host = item.reqHost } </ins> resp, err := item.transport.RoundTrip(req) if err != nil { t.Errorf("%v: Unexpected error: %v", name, err)