@@ -59,6 +59,8 @@ class DebugToolbarExtension:
5959
6060 def __init__ (self , app : Flask | None = None ) -> None :
6161 self .app = app
62+ self .toolbar_routes_host : str | None = None
63+
6264 # Support threads running `flask.copy_current_request_context` without
6365 # poping toolbar during `teardown_request`
6466 self .debug_toolbars_var : ContextVar [dict [Request , DebugToolbar ]] = ContextVar (
@@ -97,6 +99,8 @@ def init_app(self, app: Flask) -> None:
9799 "var to be set"
98100 )
99101
102+ self ._validate_and_configure_toolbar_routes_host (app )
103+
100104 DebugToolbar .load_panels (app )
101105
102106 app .before_request (self .process_request )
@@ -110,6 +114,7 @@ def init_app(self, app: Flask) -> None:
110114 "/_debug_toolbar/static/<path:filename>" ,
111115 "_debug_toolbar.static" ,
112116 self .send_static_file ,
117+ host = self .toolbar_routes_host ,
113118 )
114119
115120 app .register_blueprint (module , url_prefix = "/_debug_toolbar/views" )
@@ -118,6 +123,7 @@ def _default_config(self, app: Flask) -> dict[str, t.Any]:
118123 return {
119124 "DEBUG_TB_ENABLED" : app .debug ,
120125 "DEBUG_TB_HOSTS" : (),
126+ "DEBUG_TB_ROUTES_HOST" : None ,
121127 "DEBUG_TB_INTERCEPT_REDIRECTS" : True ,
122128 "DEBUG_TB_PANELS" : (
123129 "flask_debugtoolbar.panels.versions.VersionDebugPanel" ,
@@ -135,6 +141,61 @@ def _default_config(self, app: Flask) -> dict[str, t.Any]:
135141 "SQLALCHEMY_RECORD_QUERIES" : app .debug ,
136142 }
137143
144+ def _validate_and_configure_toolbar_routes_host (self , app : Flask ) -> None :
145+ toolbar_routes_host = app .config ["DEBUG_TB_ROUTES_HOST" ]
146+ if app .url_map .host_matching and not toolbar_routes_host :
147+ import warnings
148+
149+ warnings .warn (
150+ "Flask-DebugToolbar requires DEBUG_TB_ROUTES_HOST to be set if Flask "
151+ "is running in `host_matching` mode. Static assets for the toolbar "
152+ "will not be served correctly unless this is set." ,
153+ stacklevel = 1 ,
154+ )
155+
156+ if toolbar_routes_host :
157+ if not app .url_map .host_matching :
158+ raise ValueError (
159+ "`DEBUG_TB_ROUTES_HOST` should only be set if your Flask app is "
160+ "using `host_matching`."
161+ )
162+
163+ if toolbar_routes_host .strip () == "*" :
164+ toolbar_routes_host = "<toolbar_routes_host>"
165+ elif "<" in toolbar_routes_host and ">" in toolbar_routes_host :
166+ raise ValueError (
167+ "`DEBUG_TB_ROUTES_HOST` must either be a host name with no "
168+ "variables, to serve all Flask-DebugToolbar assets from a single "
169+ "host, or `*` to match the current request's host."
170+ )
171+
172+ # Automatically inject `toolbar_routes_host` into `url_for` calls for
173+ # the toolbar's `send_static_file` method.
174+ @app .url_defaults
175+ def inject_toolbar_routes_host_if_required (
176+ endpoint : str , values : dict [str , t .Any ]
177+ ) -> None :
178+ if app .url_map .is_endpoint_expecting (endpoint , "toolbar_routes_host" ):
179+ values .setdefault ("toolbar_routes_host" , request .host )
180+
181+ # Automatically strip `toolbar_routes_host` from the endpoint values so
182+ # that the `send_static_host` method doesn't receive that parameter,
183+ # as it's not actually required internally.
184+ @app .url_value_preprocessor
185+ def strip_toolbar_routes_host_from_static_endpoint (
186+ endpoint : str | None , values : dict [str , t .Any ] | None
187+ ) -> None :
188+ if (
189+ endpoint
190+ and values
191+ and app .url_map .is_endpoint_expecting (
192+ endpoint , "toolbar_routes_host"
193+ )
194+ ):
195+ values .pop ("toolbar_routes_host" , None )
196+
197+ self .toolbar_routes_host = toolbar_routes_host
198+
138199 def dispatch_request (self ) -> t .Any :
139200 """Modified version of ``Flask.dispatch_request`` to call
140201 :meth:`process_view`.
0 commit comments