Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect the X-Forwarded-Prefix header (for redirects) #2916

Open
bastidest opened this issue Oct 24, 2021 · 0 comments
Open

Respect the X-Forwarded-Prefix header (for redirects) #2916

bastidest opened this issue Oct 24, 2021 · 0 comments

Comments

@bastidest
Copy link

TL;DR

Add support for the X-Forwarded-Prefix for improved support for reverse proxies. Redirects should always be relative to X-Forwarded-Prefix, not only in RedirectTrailingSlash.

Description

Redirects (and maybe other response path related functions) do not respect the X-Forwarded-Prefix header. This non-standard HTTP header allows applications to be proxied under a sub-URL. For example a user-facing application may be composed of multiple services (e.g. payment, order) using gin. Requests are proxied by a reverse proxy. Both services respond to API-calls like POST /api/v1/order or GET /api/v1/payment/.... The reverse proxy delegates the requests to the respective service:

  • /static/pic.png -> serve static/pic.png
  • /orders/api/v1/order -> redirect request to order service: /api/v1/order, X-Forwarded-Prefix: /orders
  • /payments/api/v1/payment -> redirect request to payment service: /api/v1/payment, X-Forwarded-Prefix: /payments

The header should be respected in HTTP responses containing the path. A redirect should be relative to the X-Forwarded-Prefix header.

How to reproduce

The endpoints /apple and /banana redirect to /orange. /apple does not respect the X-Forwarded-Prefix header, /banana does.

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func redirectWithPrefix(c *gin.Context, destination string) {
	if prefix := c.Request.Header.Get("X-Forwarded-Prefix"); prefix != "." {
		destination = prefix + "/" + destination
	}
	c.Redirect(http.StatusTemporaryRedirect, destination)
}

func main() {
	r := gin.Default()
	r.RedirectTrailingSlash = true

	r.GET("/apple", func(c *gin.Context) {
		c.Redirect(http.StatusTemporaryRedirect, "/orange")
	})

	r.GET("/banana", func(c *gin.Context) {
		redirectWithPrefix(c, "/orange")
	})

	r.GET("/orange", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "orange",
		})
	})

	r.Run()
}

Expectations (Banana)

Redirect correctly is /foo/orange.

$ curl -v -H "X-Forwarded-Prefix: /foo" http://localhost:8080/banana 
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /banana HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
> X-Forwarded-Prefix: /foo
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 307 Temporary Redirect
< Content-Type: text/html; charset=utf-8
< Location: /foo/orange
< Date: Sun, 24 Oct 2021 14:06:31 GMT
< Content-Length: 47
< 
<a href="/foo/orange">Temporary Redirect</a>.

Actual result (Apple)

Redirect should be /foo/orange, but is /orange.

$ curl -v -H "X-Forwarded-Prefix: /foo" http://localhost:8080/apple 
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /apple HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
> X-Forwarded-Prefix: /foo
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 307 Temporary Redirect
< Content-Type: text/html; charset=utf-8
< Location: /orange
< Date: Sun, 24 Oct 2021 14:06:46 GMT
< Content-Length: 43
< 
<a href="/orange">Temporary Redirect</a>.

Related Issues / PRs / Code-Snippets:

Environment

  • go version: go version go1.17.1 linux/amd64
  • gin version (or commit ref): v1.7.4
  • operating system: Arch Linux
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant