diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md
index c52b5d571b6..52757b846d3 100644
--- a/docs/my-website/docs/proxy/config_settings.md
+++ b/docs/my-website/docs/proxy/config_settings.md
@@ -796,6 +796,7 @@ router_settings:
| REPLICATE_MODEL_NAME_WITH_ID_LENGTH | Length of Replicate model names with ID. Default is 64
| REPLICATE_POLLING_DELAY_SECONDS | Delay in seconds for Replicate polling operations. Default is 0.5
| REQUEST_TIMEOUT | Timeout in seconds for requests. Default is 6000
+| ROOT_REDIRECT_URL | URL to redirect root path (/) to when DOCS_URL is set to something other than "/" (DOCS_URL is "/" by default)
| ROUTER_MAX_FALLBACKS | Maximum number of fallbacks for router. Default is 5
| RUNWAYML_DEFAULT_API_VERSION | Default API version for RunwayML service. Default is "2024-11-06"
| RUNWAYML_POLLING_TIMEOUT | Timeout in seconds for RunwayML image generation polling. Default is 600 (10 minutes)
diff --git a/docs/my-website/docs/proxy/ui.md b/docs/my-website/docs/proxy/ui.md
index f6fa02fb69b..33033b06f85 100644
--- a/docs/my-website/docs/proxy/ui.md
+++ b/docs/my-website/docs/proxy/ui.md
@@ -6,32 +6,31 @@ import TabItem from '@theme/TabItem';
Create keys, track spend, add models without worrying about the config / CRUD endpoints.
-
-
-
-
+
## Quick Start
-- Requires proxy master key to be set
-- Requires db connected
+- Requires proxy master key to be set
+- Requires db connected
Follow [setup](./virtual_keys.md#setup)
### 1. Start the proxy
+
```bash
litellm --config /path/to/config.yaml
#INFO: Proxy running on http://0.0.0.0:4000
```
-### 2. Go to UI
+### 2. Go to UI
+
```bash
http://0.0.0.0:4000/ui # /ui
```
+### 3. Get Admin UI Link on Swagger
-### 3. Get Admin UI Link on Swagger
Your Proxy Swagger is available on the root of the Proxy: e.g.: `http://localhost:4000/`
@@ -48,9 +47,20 @@ UI_PASSWORD=langchain # password to sign in on UI
On accessing the LiteLLM UI, you will be prompted to enter your username, password
-## Invite-other users
+### 5. Configure Root Redirect URL
-Allow others to create/delete their own keys.
+When `DOCS_URL` is set to something other than `"/"`, you can configure where the root path (`/`) redirects to using `ROOT_REDIRECT_URL`:
+
+```shell
+DOCS_URL="/docs" # Set docs to a different path
+ROOT_REDIRECT_URL="/ui" # Redirect root path (/) to /ui
+```
+
+By default, `DOCS_URL` is `"/"`, so this setting is only needed when you've changed `DOCS_URL` to a different path.
+
+## Invite-other users
+
+Allow others to create/delete their own keys.
[**Go Here**](./self_serve.md)
@@ -72,11 +82,10 @@ For information on sharing models and agents, see [AI Hub](./ai_hub.md).
## Disable Admin UI
-Set `DISABLE_ADMIN_UI="True"` in your environment to disable the Admin UI.
-
-Useful, if your security team has additional restrictions on UI usage.
+Set `DISABLE_ADMIN_UI="True"` in your environment to disable the Admin UI.
+Useful, if your security team has additional restrictions on UI usage.
**Expected Response**
-
\ No newline at end of file
+
diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py
index 88eb1cec6e7..4d1a34f49c4 100644
--- a/litellm/proxy/proxy_server.py
+++ b/litellm/proxy/proxy_server.py
@@ -1084,6 +1084,13 @@ def mount_swagger_ui():
mount_swagger_ui()
+docs_url = _get_docs_url()
+root_redirect_url = os.getenv("ROOT_REDIRECT_URL")
+if docs_url != "/" and root_redirect_url:
+ @app.get("/", include_in_schema=False)
+ async def root_redirect():
+ return RedirectResponse(url=root_redirect_url)
+
from typing import Dict
user_api_base = None
diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py
index 7b99cd0b920..1b7d285bf33 100644
--- a/tests/test_litellm/proxy/test_proxy_server.py
+++ b/tests/test_litellm/proxy/test_proxy_server.py
@@ -2619,6 +2619,49 @@ def test_get_prompt_spec_for_db_prompt_with_versions():
assert prompt_spec_v2.prompt_id == "chat_prompt.v2"
+def test_root_redirect_when_docs_url_not_root_and_redirect_url_set(monkeypatch):
+ from litellm.proxy.proxy_server import cleanup_router_config_variables
+ from litellm.proxy.utils import _get_docs_url
+ from fastapi.responses import RedirectResponse
+
+ cleanup_router_config_variables()
+ filepath = os.path.dirname(os.path.abspath(__file__))
+ config_fp = f"{filepath}/test_configs/test_config_no_auth.yaml"
+ # Ensure docs are mounted on a non-root path to trigger redirect logic
+ monkeypatch.setenv("DOCS_URL", "/docs")
+
+ test_redirect_url = "/ui"
+ monkeypatch.setenv("ROOT_REDIRECT_URL", test_redirect_url)
+
+ asyncio.run(initialize(config=config_fp, debug=True))
+
+ docs_url = _get_docs_url()
+ root_redirect_url = os.getenv("ROOT_REDIRECT_URL")
+
+ # Remove any existing "/" route that might interfere
+ routes_to_remove = []
+ for route in app.routes:
+ if hasattr(route, "path") and route.path == "/":
+ if hasattr(route, "methods") and "GET" in route.methods:
+ routes_to_remove.append(route)
+ elif not hasattr(route, "methods"): # Catch-all routes
+ routes_to_remove.append(route)
+
+ for route in routes_to_remove:
+ app.routes.remove(route)
+
+ # Add the redirect route if conditions are met (matching the actual implementation)
+ if docs_url != "/" and root_redirect_url:
+ @app.get("/", include_in_schema=False)
+ async def root_redirect():
+ return RedirectResponse(url=root_redirect_url)
+
+ client = TestClient(app)
+ response = client.get("/", follow_redirects=False)
+ assert response.status_code == 307
+ assert response.headers["location"] == test_redirect_url
+
+
def test_get_image_non_root_uses_tmp_assets_dir(monkeypatch):
"""
Test that get_image uses /tmp/litellm_assets when LITELLM_NON_ROOT is true.