webdav-rfc4918
A secure-by-default WebDAV client for Python, built on RFC 4918 and RFC 5689.
Filesystem-style API, the raw protocol when you need it, a dav command, and protection against known attacks.
Full documentation: webdav.readthedocs.io
webdav-rfc4918 is a WebDAV client for Python. WebDAV lets you read and
write files on a server over HTTP, like a network drive reachable by URL.
- Tested in CI on every commit against Nextcloud, Apache (mod_dav), nginx (dav-ext) and WsgiDAV. See Tested against four servers.
- Secure defaults: TLS verification, same-origin redirects, size limits on buffered responses. See Security.
- Works with pandas, dask and anything else built on fsspec. See fsspec.
Quick Start
Requires Python 3.11+. Built on requests.
pip install webdav-rfc4918
A single call, nothing to open or close:
import webdav
auth = ("user", "password") # HTTP Basic auth
webdav.mkdir("https://webdav.example.org/Photos/", auth=auth)
webdav.upload_file("Gorilla.jpg", "https://webdav.example.org/Photos/Gorilla.jpg", auth=auth)
webdav.ls("https://webdav.example.org/Photos/", auth=auth) # a list of Resource objects
Several calls to the same server: a FileSystem keeps the connection open.
with webdav.FileSystem("https://webdav.example.org", auth=auth) as fs:
fs.upload_file("Gorilla.jpg", "Photos/Gorilla.jpg")
print(fs.ls("Photos"))
fs.download_file("Photos/Gorilla.jpg", "copy.jpg")
with fs.open("Photos/Gorilla.jpg", "rb") as f: # a file-like object
image_bytes = f.read()
Need the raw protocol: status codes, headers, the multi-status body? Use a
Session directly.
with webdav.Session("https://webdav.example.org", auth=auth) as session:
response = session.propfind("/Photos/", depth=1)
print(response.status_code, list(response.multistatus.responses))
A client certificate (mTLS) and a private CA:
with webdav.Session(
"https://webdav.example.org",
cert=("client.crt", "client.key"),
verify="ca-bundle.pem",
) as session:
session.propfind("/", depth=0)
Tested against four servers
Nextcloud, Apache mod_dav, and nginx dav-ext each get their own CI job,
because each reads the RFC differently, and each is documented separately.
WsgiDAV runs the rest of the test suite.
| Server | CI (main) | Tests | Docs |
|---|---|---|---|
| Nextcloud | 40+ | docs | |
Apache mod_dav |
100+ | docs | |
nginx + dav-ext |
25+ | docs | |
| WsgiDAV | rest of the suite | - |
The test suite runs on every commit: 1500+ tests with over 90% code coverage. It includes:
- fsspec's own conformance suite (130+ tests)
- 130+ tests that each check one clause of RFC 4918
- 70+ tests for the security defaults described below
Security
A WebDAV server, or a redirect to one, can be hostile. This is the complete list of defaults:
- TLS verification is on. Turning it off (
verify=False, or anythingrequestsreads as false) emits and logs aTLSHardeningDisabledWarningthaturllib3.disable_warnings()does not silence. So does aTLSOptionswith a TLS version below 1.2 or with strict chain checking off. REQUESTS_CA_BUNDLEandCURL_CA_BUNDLEare ignored, so the environment cannot replace the CA you configured.~/.netrcis ignored too: withoutauth=, no credentials are sent.- A URL with credentials in it (
https://user:pw@host/) is refused. Passauth=instead. - Credentials sent over plain
httpto a host other than localhost trigger anInsecureTransportWarning, once per host. - Only same-origin redirects are followed
(
RedirectPolicy.SAME_ORIGIN). Credentials and cookies are never sent to another origin, even a trusted one. - A redirect from
httpstohttpis never followed, under any policy. - Buffered responses, including multistatus XML, are size-capped, and so are redirect chains. Exact limits: Session reference.
- A request that is not streamed has a deadline for the whole exchange
(
max_response_time, default 300 s): connecting, every redirect hop, headers and body.timeoutonly limits each single read. - A streamed download (
stream=True,download_file) is bounded per read only. Neithermax_response_sizenormax_response_timeapplies to it. - XML from the server is parsed with the standard library's expat parser.
External entities are never resolved, and expat 2.4.0 or newer rejects
entity expansion attacks such as "billion laughs". A multistatus or lock
response that tries either raises
MalformedResponseError. - Credentials do not end up in exception messages, warnings or logs: the userinfo of a URL and the query of a signed URL are redacted.
download_filewrites only to the path you give, through a temporary file, and refuses a symlink at that path.
Report a vulnerability: SECURITY.md.
fsspec
fsspec is the storage interface
behind pandas, dask and most of the Python data ecosystem. This package adds
a WebDAV backend, so those tools can read and write a WebDAV server like any
other storage. It passes fsspec's own conformance suite (130+ tests).
pip install webdav-rfc4918[fsspec]
import fsspec
import pandas as pd
auth = ("user", "password")
df = pd.read_csv("webdavs://webdav.example.org/data.csv", storage_options={"auth": auth})
with fsspec.open("webdavs://webdav.example.org/Photos/Gorilla.jpg", auth=auth) as f:
f.read()
Installing the package registers the webdav and webdavs schemes with
fsspec. Paths start at the root of the server (/Photos/Gorilla.jpg). See
fsspec for URL schemes, credentials and path
semantics.
Locking
Lock a file while you edit it, so no one else can overwrite it (class 2
locking, RFC 4918 §7). The If header is handled for you:
import webdav
auth = ("user", "password")
with webdav.FileSystem("https://webdav.example.org", auth=auth) as fs:
with fs.locked("Documents/report.docx") as lock:
# writes to the locked path carry the lock token automatically
fs.upload_file("report.docx", "Documents/report.docx", overwrite=True)
fs.refresh_lock("Documents/report.docx", lock.token)
A lock times out, and nothing refreshes it for you: writes after that fail
with 412. See
Locking for the details.
Command line
The dav command is part of the package:
dav ls webdavs://webdav.example.org/Photos
dav get webdavs://webdav.example.org/report.pdf ./report.pdf
dav put ./report.pdf webdavs://webdav.example.org/report.pdf --overwrite
webdavs:// is WebDAV over HTTPS, webdav:// plain HTTP.
Also info, cat, mkdir, rm, mv and cp. Credentials via --user and
--password, or $WEBDAV_USER and $WEBDAV_PASSWORD. See the
CLI reference, or dav <command> --help.
More
The documentation has a quickstart, reference pages for sessions, TLS and redirects, and migration guides from webdav4, webdavclient3 and other WebDAV clients. Release history: CHANGELOG.md. Licensed under the MIT License.
Metadata
Release files for webdav-rfc4918 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| webdav_rfc4918-1.1.0.tar.gz | 324.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| webdav_rfc4918-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 470.8 kB
Release files / webdav_rfc4918-1.1.0.tar.gz
| Download URL | webdav_rfc4918-1.1.0.tar.gz |
|---|---|
| Size | 324.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7ff23069e8dff80257639aca5e9ab9bbc6ea898cc07f5e1f222d521f4cbbac33
|
|
BLAKE2b-256 checksum How to use checksums |
638927b8a6ca048c0b95831431c36c589be56b3dd1f0d04139926b8422042b4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / webdav_rfc4918-1.1.0-py3-none-any.whl
| Download URL | webdav_rfc4918-1.1.0-py3-none-any.whl |
|---|---|
| Size | 146.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3ab6537f54eff2d4ef19c4e6c502d46686387c53fd0df424b855945fd1ca8b6e
|
|
BLAKE2b-256 checksum How to use checksums |
280579e443f08d5151732f9ad59124d94eaf6ef0b66447bef0bd56753786ec26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log