From 63c2b692597a384168f8383a58aa590430b04c92 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 24 Mar 2026 07:19:08 +0800 Subject: [PATCH] Make PUBLIC_URL_DETECTION default to "auto" (#36955) Related issues including: #36939 , #35619, #34950 , #34253 , #32554 For users who use reverse-proxy, we have documented the requirements clearly since long time ago : https://docs.gitea.com/administration/reverse-proxies --- custom/conf/app.example.ini | 9 ++++----- modules/setting/server.go | 2 +- routers/web/admin/admin_test.go | 23 +++++++++++++++++------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 5eb4a5e9956..b752a81ca93 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -69,13 +69,12 @@ RUN_USER = ; git ;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy. ;ROOT_URL = ;; -;; Controls how to detect the public URL. -;; Although it defaults to "legacy" (to avoid breaking existing users), most instances should use the "auto" behavior, +;; Controls how to detect the public URL. Most instances should use the "auto" behavior, ;; especially when the Gitea instance needs to be accessed in a container network. -;; * legacy: detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL". -;; * auto: always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL". +;; * legacy: (default <= 1.25) detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL". +;; * auto: (default >= 1.26) always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL". ;; * never: always use "ROOT_URL", never detect from request headers. -;PUBLIC_URL_DETECTION = legacy +;PUBLIC_URL_DETECTION = auto ;; ;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy. ;; DO NOT USE IT IN PRODUCTION!!! diff --git a/modules/setting/server.go b/modules/setting/server.go index 7e7611b802d..f0fbbce970a 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -286,7 +286,7 @@ func loadServerFrom(rootCfg ConfigProvider) { defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL) - PublicURLDetection = sec.Key("PUBLIC_URL_DETECTION").MustString(PublicURLLegacy) + PublicURLDetection = sec.Key("PUBLIC_URL_DETECTION").MustString(PublicURLAuto) if PublicURLDetection != PublicURLAuto && PublicURLDetection != PublicURLLegacy && PublicURLDetection != PublicURLNever { log.Fatal("Invalid PUBLIC_URL_DETECTION value: %s", PublicURLDetection) } diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go index a568c7c5c81..ecdd462f9e7 100644 --- a/routers/web/admin/admin_test.go +++ b/routers/web/admin/admin_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestShadowPassword(t *testing.T) { @@ -74,19 +75,29 @@ func TestShadowPassword(t *testing.T) { } func TestSelfCheckPost(t *testing.T) { + defer test.MockVariableValue(&setting.PublicURLDetection)() defer test.MockVariableValue(&setting.AppURL, "http://config/sub/")() defer test.MockVariableValue(&setting.AppSubURL, "/sub")() - ctx, resp := contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend") - SelfCheckPost(ctx) - assert.Equal(t, http.StatusOK, resp.Code) - data := struct { Problems []string `json:"problems"` }{} - err := json.Unmarshal(resp.Body.Bytes(), &data) - assert.NoError(t, err) + + setting.PublicURLDetection = setting.PublicURLLegacy + ctx, resp := contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend") + SelfCheckPost(ctx) + assert.Equal(t, http.StatusOK, resp.Code) + require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data)) assert.Equal(t, []string{ ctx.Locale.TrString("admin.self_check.location_origin_mismatch", "http://frontend/sub/", "http://config/sub/"), }, data.Problems) + + setting.PublicURLDetection = setting.PublicURLAuto + ctx, resp = contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend") + SelfCheckPost(ctx) + assert.Equal(t, http.StatusOK, resp.Code) + require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data)) + assert.Equal(t, []string{ + ctx.Locale.TrString("admin.self_check.location_origin_mismatch", "http://frontend/sub/", "http://host/sub/"), + }, data.Problems) }