Browse and ingest files from any WebDAV server — Nextcloud, ownCloud, Apache
mod_dav, sabre/dav, and other RFC 4918 compliant servers — into Dify as an
online drive datasource. Navigate folders, pick files, and Dify downloads
and ingests them.
Configure the datasource in Dify with three credentials:
| Field | Required | Description |
|---|---|---|
base_url |
yes | The WebDAV endpoint URL (a collection/folder). |
username |
no | WebDAV account username (omit for anonymous servers). |
password |
no | WebDAV account password or app token (stored as a secret). |
Nextcloud and ownCloud expose per-user WebDAV under /remote.php/dav/files/:
https://example.com/remote.php/dav/files/<user>/
For a generic Apache mod_dav share the base_url is simply the share URL,
for example https://example.com/dav/.
Credentials are validated on save with a PROPFIND request against base_url.
- Browse issues a
Depth: 1PROPFINDagainst the selected folder and parses the multistatus XML into folders and files (name, size, type). - Download issues a plain
GETfor the chosen file and streams its bytes (with the server'sContent-Type) back to Dify for ingestion. - Folder navigation uses the resource path as the id; files use their full server path.
The WebDAV protocol logic lives in the webdav_client/ package (a thin
PROPFIND parser and HTTP client built on requests + lxml), independent of
the Dify SDK and unit-tested with mocked HTTP. The datasource class is a thin
adapter over the SDK's OnlineDriveDatasource interface.
- A broken or partial XML response raises rather than returning an empty
listing. The
PROPFINDparser uses a hardened lxml configuration (no DTD, no entity resolution, no network, no huge trees) and rejects any document carrying aDOCTYPE(XXE / "billion laughs" defence). A syntactically broken multistatus body now raises an error instead of silently degrading to a partial or empty list — so a malfunctioning server is distinguishable from a genuinely empty folder. An empty folder is a well-formed multistatus that lists only the collection itself (which is excluded), yielding[]. - Redirects are intentionally not followed (
allow_redirects=False) as an SSRF guard, so a legitimate301/302from the server surfaces as an "unexpected response" error rather than being chased. Pointbase_urlat the final, canonical collection URL. - The pagination cursor re-reads the directory between pages. WebDAV
PROPFINDhas no server-side paging, so each page re-lists the folder and slices it client-side. If the folder changes mid-crawl you can get duplicate or skipped entries between pages — acceptable for ingest, but not a point-in-time snapshot. - Credentials are never echoed in error text. If
base_urlembedsuser:pass@, anyuser:pass@userinfo is stripped from error messages before they reach Dify / the LLM.
This plugin uses uv with pyproject.toml +
uv.lock (there is no requirements.txt).
uv sync
uv run ruff check .
uv run pytest -qWithout uv, create a virtualenv and install the runtime + dev tools manually:
python -m venv .venv
. .venv/bin/activate
pip install -e . ruff pytest
ruff check .
python -m pytest -qApache-2.0 © 2026 Alexey Shabalin