Skip to content

Commit 4cd475f

Browse files
committed
Add config option "USE_HOST_HEADER"
Resolves #32554 This option can be set to make Gitea always use the "Host" request header for construction of absolute URLs.
1 parent 5eb0ee4 commit 4cd475f

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

modules/httplib/url.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,19 @@ func GuessCurrentHostURL(ctx context.Context) string {
6969
// 1. The reverse proxy is configured correctly, it passes "X-Forwarded-Proto/Host" headers. Perfect, Gitea can handle it correctly.
7070
// 2. The reverse proxy is not configured correctly, doesn't pass "X-Forwarded-Proto/Host" headers, eg: only one "proxy_pass http://gitea:3000" in Nginx.
7171
// 3. There is no reverse proxy.
72-
// Without an extra config option, Gitea is impossible to distinguish between case 2 and case 3,
73-
// then case 2 would result in wrong guess like guessed AppURL becomes "http://gitea:3000/", which is not accessible by end users.
74-
// So in the future maybe it should introduce a new config option, to let site admin decide how to guess the AppURL.
72+
// With the "USE_HOST_HEADER" config option disabled (default), Gitea is impossible to distinguish between case 2 and case 3,
73+
// When enabling "USE_HOST_HEADER", any reverse proxies must be configured to properly pass "X-Forwarded-Proto/Host" headers,
74+
// otherwise this would result in wrong guess like guessed AppURL becomes "http://gitea:3000/", which is not accessible by end users.
7575
reqScheme := getRequestScheme(req)
7676
if reqScheme == "" {
77+
if setting.UseHostHeader && req.Host != "" {
78+
if req.TLS == nil {
79+
return "http://" + req.Host
80+
} else {
81+
return "https://" + req.Host
82+
}
83+
}
84+
7785
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
7886
}
7987
// X-Forwarded-Host has many problems: non-standard, not well-defined (X-Forwarded-Port or not), conflicts with Host header.

modules/httplib/url_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package httplib
55

66
import (
77
"context"
8+
"crypto/tls"
89
"net/http"
910
"testing"
1011

@@ -39,6 +40,32 @@ func TestIsRelativeURL(t *testing.T) {
3940
}
4041
}
4142

43+
func TestGuessCurrentHostURL(t *testing.T) {
44+
defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()
45+
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
46+
47+
ctx := context.Background()
48+
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
49+
50+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
51+
Host: "localhost:3000",
52+
})
53+
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
54+
55+
defer test.MockVariableValue(&setting.UseHostHeader, true)()
56+
57+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
58+
Host: "localhost:3000",
59+
})
60+
assert.Equal(t, "http://localhost:3000", GuessCurrentHostURL(ctx))
61+
62+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
63+
Host: "localhost",
64+
TLS: &tls.ConnectionState{},
65+
})
66+
assert.Equal(t, "https://localhost", GuessCurrentHostURL(ctx))
67+
}
68+
4269
func TestMakeAbsoluteURL(t *testing.T) {
4370
defer test.MockVariableValue(&setting.Protocol, "http")()
4471
defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()

modules/setting/server.go

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ var (
5050
AppSubURL string
5151
// UseSubURLPath makes Gitea handle requests with sub-path like "/sub-path/owner/repo/...", to make it easier to debug sub-path related problems without a reverse proxy.
5252
UseSubURLPath bool
53+
// UseHostHeader makes Gitea always use the "Host" request header for construction of absolute URLs.
54+
// This requires any reverse proxy to properly pass headers like "X-Forwarded-Proto" and "Host".
55+
// It maps to ini:"USE_HOST_HEADER" in [server] and defaults to false
56+
UseHostHeader bool
5357
// AppDataPath is the default path for storing data.
5458
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
5559
AppDataPath string
@@ -277,6 +281,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
277281
// This value is empty if site does not have sub-url.
278282
AppSubURL = strings.TrimSuffix(appURL.Path, "/")
279283
UseSubURLPath = sec.Key("USE_SUB_URL_PATH").MustBool(false)
284+
UseHostHeader = sec.Key("USE_HOST_HEADER").MustBool(false)
280285
StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
281286

282287
// Check if Domain differs from AppURL domain than update it to AppURL's domain

0 commit comments

Comments
 (0)