--- a/jupyterlab_server/handlers.py +++ b/jupyterlab_server/handlers.py @@ -218,13 +218,17 @@ # Handle federated lab extensions. labextensions_path = extension_app.extra_labextensions_path + extension_app.labextensions_path labextensions_url = ujoin(extension_app.labextensions_url, "(.*)") - handlers.append( - ( - labextensions_url, - FileFindHandler, - {"path": labextensions_path, "no_cache_paths": no_cache_paths}, - ) - ) + labextensions_kwargs: dict[str, Any] = { + "path": labextensions_path, + "no_cache_paths": no_cache_paths, + } + if hasattr(FileFindHandler, "follow_dir_symlinks"): + # `jupyter labextension develop` links the extension in from its source tree, + # and tornado >= 6.5.9 will not serve through such a link unless the handler + # asks for it. Only this route asks; /static/ and /custom/ stay strict. + # Older jupyter_server has no such option and leaves this out. + labextensions_kwargs["follow_dir_symlinks"] = True + handlers.append((labextensions_url, FileFindHandler, labextensions_kwargs)) # Handle local settings. if extension_app.schemas_dir: