diff --git a/airflow-core/tests/unit/always/test_project_structure.py b/airflow-core/tests/unit/always/test_project_structure.py
index af628f4b32c3a..a1cfba80cf693 100644
--- a/airflow-core/tests/unit/always/test_project_structure.py
+++ b/airflow-core/tests/unit/always/test_project_structure.py
@@ -138,6 +138,7 @@ def test_providers_modules_should_have_tests(self):
"providers/fab/tests/unit/fab/www/extensions/test_init_security.py",
"providers/fab/tests/unit/fab/www/extensions/test_init_session.py",
"providers/fab/tests/unit/fab/www/extensions/test_init_views.py",
+ "providers/fab/tests/unit/fab/www/extensions/test_init_wsgi_middlewares.py",
"providers/fab/tests/unit/fab/www/security/test_permissions.py",
"providers/fab/tests/unit/fab/www/test_airflow_flask_app.py",
"providers/fab/tests/unit/fab/www/test_app.py",
diff --git a/providers/fab/provider.yaml b/providers/fab/provider.yaml
index c069b3b12bda4..7805ca3a0d772 100644
--- a/providers/fab/provider.yaml
+++ b/providers/fab/provider.yaml
@@ -119,6 +119,58 @@ config:
type: integer
example: ~
default: "43200"
+ enable_proxy_fix:
+ description: |
+ Enable werkzeug ``ProxyFix`` middleware for reverse proxy
+ version_added: 2.1.0
+ type: boolean
+ example: ~
+ default: "False"
+ proxy_fix_x_for:
+ description: |
+ Number of values to trust for ``X-Forwarded-For``.
+ See `Werkzeug: X-Forwarded-For Proxy Fix
+ `__ for more details.
+ version_added: 2.1.0
+ type: integer
+ example: ~
+ default: "1"
+ proxy_fix_x_proto:
+ description: |
+ Number of values to trust for ``X-Forwarded-Proto``.
+ See `Werkzeug: X-Forwarded-For Proxy Fix
+ `__ for more details.
+ version_added: 2.1.0
+ type: integer
+ example: ~
+ default: "1"
+ proxy_fix_x_host:
+ description: |
+ Number of values to trust for ``X-Forwarded-Host``.
+ See `Werkzeug: X-Forwarded-For Proxy Fix
+ `__ for more details.
+ version_added: 2.1.0
+ type: integer
+ example: ~
+ default: "1"
+ proxy_fix_x_port:
+ description: |
+ Number of values to trust for ``X-Forwarded-Port``.
+ See `Werkzeug: X-Forwarded-For Proxy Fix
+ `__ for more details.
+ version_added: 2.1.0
+ type: integer
+ example: ~
+ default: "1"
+ proxy_fix_x_prefix:
+ description: |
+ Number of values to trust for ``X-Forwarded-Prefix``.
+ See `Werkzeug: X-Forwarded-For Proxy Fix
+ `__ for more details.
+ version_added: 2.1.0
+ type: integer
+ example: ~
+ default: "1"
auth-managers:
- airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager
diff --git a/providers/fab/src/airflow/providers/fab/get_provider_info.py b/providers/fab/src/airflow/providers/fab/get_provider_info.py
index 924dfca466a39..6558807420aa7 100644
--- a/providers/fab/src/airflow/providers/fab/get_provider_info.py
+++ b/providers/fab/src/airflow/providers/fab/get_provider_info.py
@@ -79,6 +79,48 @@ def get_provider_info():
"example": None,
"default": "43200",
},
+ "enable_proxy_fix": {
+ "description": "Enable werkzeug ``ProxyFix`` middleware for reverse proxy\n",
+ "version_added": "2.1.0",
+ "type": "boolean",
+ "example": None,
+ "default": "False",
+ },
+ "proxy_fix_x_for": {
+ "description": "Number of values to trust for ``X-Forwarded-For``.\nSee `Werkzeug: X-Forwarded-For Proxy Fix\n`__ for more details.\n",
+ "version_added": "2.1.0",
+ "type": "integer",
+ "example": None,
+ "default": "1",
+ },
+ "proxy_fix_x_proto": {
+ "description": "Number of values to trust for ``X-Forwarded-Proto``.\nSee `Werkzeug: X-Forwarded-For Proxy Fix\n`__ for more details.\n",
+ "version_added": "2.1.0",
+ "type": "integer",
+ "example": None,
+ "default": "1",
+ },
+ "proxy_fix_x_host": {
+ "description": "Number of values to trust for ``X-Forwarded-Host``.\nSee `Werkzeug: X-Forwarded-For Proxy Fix\n`__ for more details.\n",
+ "version_added": "2.1.0",
+ "type": "integer",
+ "example": None,
+ "default": "1",
+ },
+ "proxy_fix_x_port": {
+ "description": "Number of values to trust for ``X-Forwarded-Port``.\nSee `Werkzeug: X-Forwarded-For Proxy Fix\n`__ for more details.\n",
+ "version_added": "2.1.0",
+ "type": "integer",
+ "example": None,
+ "default": "1",
+ },
+ "proxy_fix_x_prefix": {
+ "description": "Number of values to trust for ``X-Forwarded-Prefix``.\nSee `Werkzeug: X-Forwarded-For Proxy Fix\n`__ for more details.\n",
+ "version_added": "2.1.0",
+ "type": "integer",
+ "example": None,
+ "default": "1",
+ },
},
}
},
diff --git a/providers/fab/src/airflow/providers/fab/www/app.py b/providers/fab/src/airflow/providers/fab/www/app.py
index be29f28bd674c..873e7adb8f048 100644
--- a/providers/fab/src/airflow/providers/fab/www/app.py
+++ b/providers/fab/src/airflow/providers/fab/www/app.py
@@ -41,6 +41,7 @@
init_error_handlers,
init_plugins,
)
+from airflow.providers.fab.www.extensions.init_wsgi_middlewares import init_wsgi_middleware
from airflow.providers.fab.www.utils import get_session_lifetime_config
app: Flask | None = None
@@ -103,6 +104,7 @@ def create_app(enable_plugins: bool):
init_jinja_globals(flask_app, enable_plugins=enable_plugins)
init_xframe_protection(flask_app)
init_airflow_session_interface(flask_app)
+ init_wsgi_middleware(flask_app)
return flask_app
diff --git a/providers/fab/src/airflow/providers/fab/www/extensions/init_wsgi_middlewares.py b/providers/fab/src/airflow/providers/fab/www/extensions/init_wsgi_middlewares.py
new file mode 100644
index 0000000000000..bcb9d3919f6c3
--- /dev/null
+++ b/providers/fab/src/airflow/providers/fab/www/extensions/init_wsgi_middlewares.py
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from werkzeug.middleware.proxy_fix import ProxyFix
+
+from airflow.configuration import conf
+
+if TYPE_CHECKING:
+ from flask import Flask
+
+
+def init_wsgi_middleware(flask_app: Flask) -> None:
+ """Handle X-Forwarded-* headers and base_url support."""
+ # Apply ProxyFix middleware
+ if conf.getboolean("fab", "ENABLE_PROXY_FIX"):
+ flask_app.wsgi_app = ProxyFix( # type: ignore
+ flask_app.wsgi_app,
+ x_for=conf.getint("fab", "PROXY_FIX_X_FOR", fallback=1),
+ x_proto=conf.getint("fab", "PROXY_FIX_X_PROTO", fallback=1),
+ x_host=conf.getint("fab", "PROXY_FIX_X_HOST", fallback=1),
+ x_port=conf.getint("fab", "PROXY_FIX_X_PORT", fallback=1),
+ x_prefix=conf.getint("fab", "PROXY_FIX_X_PREFIX", fallback=1),
+ )