diff --git a/docs/docs/Configuration/configuration-cli.md b/docs/docs/Configuration/configuration-cli.md
index 10f2cb679..cb4c286fa 100644
--- a/docs/docs/Configuration/configuration-cli.md
+++ b/docs/docs/Configuration/configuration-cli.md
@@ -122,6 +122,8 @@ python -m langflow run [OPTIONS]
| `--auto-saving-interval` | `1000` | Integer | Set the interval for flow auto-saving in milliseconds. See [`LANGFLOW_AUTO_SAVING_INTERVAL` variable](./environment-variables.md#LANGFLOW_AUTO_SAVING_INTERVAL). |
| `--health-check-max-retries` | `5` | Integer | Set the maximum number of retries for the health check. Use `--no-health-check-max-retries` to disable the maximum number of retries for the health check. See [`LANGFLOW_HEALTH_CHECK_MAX_RETRIES` variable](./environment-variables.md#LANGFLOW_HEALTH_CHECK_MAX_RETRIES). |
| `--max-file-size-upload` | `100` | Integer | Set the maximum file size for the upload in megabytes. See [`LANGFLOW_MAX_FILE_SIZE_UPLOAD` variable](./environment-variables.md#LANGFLOW_MAX_FILE_SIZE_UPLOAD). |
+| `--ssl-cert-file-path` | Not set | String | Path to the SSL certificate file on the local system. |
+| `--ssl-key-file-path` | Not set | String | Path to the SSL key file on the local system. |
| `--help` | *Not applicable* | *Not applicable* | Display information about the command usage and its options and arguments. |
### langflow superuser
diff --git a/docs/docs/Configuration/environment-variables.md b/docs/docs/Configuration/environment-variables.md
index 63121f1e2..a76fd9479 100644
--- a/docs/docs/Configuration/environment-variables.md
+++ b/docs/docs/Configuration/environment-variables.md
@@ -226,9 +226,11 @@ The following table lists the environment variables supported by Langflow.
| LANGFLOW_LOAD_FLOWS_PATH | String | Not set | Path to a directory containing flow JSON files to be loaded on startup. Note that this feature only works if LANGFLOW_AUTO_LOGIN is enabled. |
| LANGFLOW_WORKER_TIMEOUT | Integer | `300` | Worker timeout in seconds. See [`--worker-timeout` option](./configuration-cli.md#run-worker-timeout). |
| LANGFLOW_WORKERS | Integer | `1` | Number of worker processes. See [`--workers` option](./configuration-cli.md#run-workers). |
-
+| LANGFLOW_SSL_CERT_FILE | String | Not set | Path to the SSL certificate file on the local system. |
+| LANGFLOW_SSL_KEY_FILE | String | Not set | Path to the SSL key file on the local system. |
+
## Configure .env, override.conf, and tasks.json files
The following examples show how to configure Langflow using environment variables in different scenarios.
diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py
index 40e083698..aec731fa7 100644
--- a/src/backend/base/langflow/__main__.py
+++ b/src/backend/base/langflow/__main__.py
@@ -161,6 +161,10 @@ def run(
help="Defines the polling interval for the webhook.",
show_default=False,
),
+ ssl_cert_file_path: str | None = typer.Option(
+ None, help="Defines the SSL certificate file path.", show_default=False
+ ),
+ ssl_key_file_path: str | None = typer.Option(None, help="Defines the SSL key file path.", show_default=False),
) -> None:
"""Run Langflow."""
# Register SIGTERM handler
@@ -202,6 +206,8 @@ def run(
log_level = settings_service.settings.log_level
frontend_path = settings_service.settings.frontend_path
backend_only = settings_service.settings.backend_only
+ ssl_cert_file_path = settings_service.settings.ssl_cert_file if ssl_cert_file_path is None else ssl_cert_file_path
+ ssl_key_file_path = settings_service.settings.ssl_key_file if ssl_key_file_path is None else ssl_key_file_path
# create path object if frontend_path is provided
static_files_dir: Path | None = Path(frontend_path) if frontend_path else None
@@ -215,7 +221,10 @@ def run(
"bind": f"{host}:{port}",
"workers": get_number_of_workers(workers),
"timeout": worker_timeout,
+ "certfile": ssl_cert_file_path,
+ "keyfile": ssl_key_file_path,
}
+ protocol = "https" if options["keyfile"] and options["certfile"] else "http"
# Define an env variable to know if we are just testing the server
if "pytest" in sys.modules:
@@ -226,10 +235,10 @@ def run(
# Run using uvicorn on MacOS and Windows
# Windows doesn't support gunicorn
# MacOS requires an env variable to be set to use gunicorn
- run_on_windows(host, port, log_level, options, app)
+ run_on_windows(host, port, log_level, options, app, protocol)
else:
# Run using gunicorn on Linux
- process = run_on_mac_or_linux(host, port, log_level, options, app)
+ process = run_on_mac_or_linux(host, port, log_level, options, app, protocol)
if open_browser and not backend_only:
click.launch(f"http://{host}:{port}")
if process:
@@ -250,12 +259,14 @@ def run(
raise typer.Exit(1) from e
-def wait_for_server_ready(host, port) -> None:
+def wait_for_server_ready(host, port, protocol) -> None:
"""Wait for the server to become ready by polling the health endpoint."""
status_code = 0
while status_code != httpx.codes.OK:
try:
- status_code = httpx.get(f"http://{host}:{port}/health").status_code
+ status_code = httpx.get(
+ f"{protocol}://{host}:{port}/health", verify=host not in ("127.0.0.1", "localhost")
+ ).status_code
except HTTPError:
time.sleep(1)
except Exception: # noqa: BLE001
@@ -263,18 +274,18 @@ def wait_for_server_ready(host, port) -> None:
time.sleep(1)
-def run_on_mac_or_linux(host, port, log_level, options, app):
+def run_on_mac_or_linux(host, port, log_level, options, app, protocol):
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
webapp_process.start()
- wait_for_server_ready(host, port)
+ wait_for_server_ready(host, port, protocol)
- print_banner(host, port)
+ print_banner(host, port, protocol)
return webapp_process
-def run_on_windows(host, port, log_level, options, app) -> None:
+def run_on_windows(host, port, log_level, options, app, protocol) -> None:
"""Run the Langflow server on Windows."""
- print_banner(host, port)
+ print_banner(host, port, protocol)
run_langflow(host, port, log_level, options, app)
@@ -358,7 +369,7 @@ def stylize_text(text: str, to_style: str, *, is_prerelease: bool) -> str:
return text.replace(to_style, styled_text)
-def print_banner(host: str, port: int) -> None:
+def print_banner(host: str, port: int, protocol: str) -> None:
notices = []
package_names = [] # Track package names for pip install instructions
is_pre_release = False # Track if any package is a pre-release
@@ -398,7 +409,7 @@ def print_banner(host: str, port: int) -> None:
"We collect anonymous usage data to improve Langflow.\n"
"You can opt-out by setting [bold]DO_NOT_TRACK=true[/bold] in your environment."
)
- access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
+ access_link = f"Access [link={protocol}://{host}:{port}]{protocol}://{host}:{port}[/link]"
panel_content = "\n\n".join([title, *styled_notices, info_text, telemetry_text, access_link])
panel = Panel(panel_content, box=box.ROUNDED, border_style="blue", expand=False)
@@ -416,6 +427,8 @@ def run_langflow(host, port, log_level, options, app) -> None:
port=port,
log_level=log_level.lower(),
loop="asyncio",
+ ssl_keyfile=options["keyfile"],
+ ssl_certfile=options["certfile"],
)
else:
from langflow.server import LangflowApplication
diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py
index e89252e9c..e35c7b6f0 100644
--- a/src/backend/base/langflow/services/settings/base.py
+++ b/src/backend/base/langflow/services/settings/base.py
@@ -224,6 +224,10 @@ class Settings(BaseSettings):
"""The polling interval for the webhook in ms."""
fs_flows_polling_interval: int = 10000
"""The polling interval in milliseconds for synchronizing flows from the file system."""
+ ssl_cert_file: str | None = None
+ """Path to the SSL certificate file on the local system."""
+ ssl_key_file: str | None = None
+ """Path to the SSL key file on the local system."""
# MCP Server
mcp_server_enabled: bool = True