NGINX includes a built-in DAV module that handles basic file operations like uploading and deleting files. However, this standard module only supports a subset of the WebDAV protocol. The NGINX WebDAV module (ngx_http_dav_ext_module) adds the missing PROPFIND, OPTIONS, LOCK, and UNLOCK methods that clients such as macOS Finder, Windows Explorer, and Cyberduck require for proper WebDAV access. Together with the standard module, the NGINX WebDAV module turns NGINX into a fully compliant WebDAV file server.
In this guide, you will learn how to install, configure, and secure a production-ready NGINX WebDAV server using both the standard DAV module and the NGINX WebDAV module together.
Why You Need the NGINX WebDAV Module
WebDAV (Web Distributed Authoring and Versioning) extends HTTP to allow clients to create, move, copy, and delete files on a remote server. It is widely used for file sharing, cloud storage, and collaborative editing. Applications that rely on WebDAV include macOS Finder’s “Connect to Server” feature, Windows mapped network drives, Cyberduck, and many mobile file manager apps.
NGINX’s standard ngx_http_dav_module supports only five write methods: PUT, DELETE, MKCOL, COPY, and MOVE. These handle file uploads and directory management, but they are not enough for a working WebDAV server. Most WebDAV clients need PROPFIND to list directory contents, OPTIONS to discover supported methods, and LOCK/UNLOCK for safe concurrent editing.
The following table shows what each module provides:
| Method | Standard DAV Module | DAV Extensions Module |
|---|---|---|
| GET, HEAD | NGINX core | — |
| PUT | Yes | — |
| DELETE | Yes | — |
| MKCOL | Yes | — |
| COPY | Yes | — |
| MOVE | Yes | — |
| PROPFIND | — | Yes |
| OPTIONS | — | Yes |
| LOCK | — | Yes |
| UNLOCK | — | Yes |
You need both modules working together to provide full WebDAV support in NGINX. The standard module handles file write operations, while the NGINX WebDAV module handles property queries, capability discovery, and resource locking.
Installing the NGINX WebDAV Module
RHEL, CentOS, AlmaLinux, Rocky Linux
Install the GetPageSpeed repository and the module package:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-dav-ext
Load the module by adding the following directive at the top of /etc/nginx/nginx.conf, before any http block:
load_module modules/ngx_http_dav_ext_module.so;
The standard ngx_http_dav_module is already compiled into NGINX by default, so no additional load_module directive is needed for it.
Debian and Ubuntu
First, set up the GetPageSpeed APT repository, then install the module:
sudo apt-get update
sudo apt-get install nginx-module-dav-ext
On Debian/Ubuntu, the package handles module loading automatically. No
load_moduledirective is needed.
You can find detailed package information on the RPM module page or the APT module page.
Configuration Directives
The NGINX WebDAV module provides three directives. All directive names and their parameters are verified against the module source code.
dav_ext_methods
Enables support for the specified WebDAV methods in the current scope.
Syntax: dav_ext_methods [PROPFIND] [OPTIONS] [LOCK] [UNLOCK] | off
Default: off
Context: http, server, location
You can enable any combination of the four methods. The off value explicitly disables all extended methods and is the default behavior. If you only need directory listing without locking, enable just PROPFIND and OPTIONS. If you need full WebDAV support with concurrent editing, enable all four methods.
dav_ext_lock_zone
Defines a shared memory zone for storing WebDAV locks and sets the lock expiration timeout.
Syntax: dav_ext_lock_zone zone=NAME:SIZE [timeout=TIMEOUT]
Default: —
Context: http
Parameters:
zone=NAME:SIZE— The name and size of the shared memory zone used to store active locks. For example,zone=davlock:10mallocates 10 MB of shared memory. Each lock entry is small, so 10 MB can hold thousands of simultaneous locks.timeout=TIMEOUT— The time after which a lock automatically expires if not refreshed. The default value is60seconds. WebDAV clients typically refresh locks before they expire, but a longer timeout (e.g.,timeout=300for 5 minutes) reduces the frequency of refresh requests.
This directive must be placed in the http context because the shared memory zone is global across all server blocks.
dav_ext_lock
Enables WebDAV locking in the specified scope and links it to a shared memory zone.
Syntax: dav_ext_lock zone=NAME
Default: —
Context: http, server, location
The zone=NAME parameter must reference a zone previously defined by dav_ext_lock_zone. You must also enable the LOCK and UNLOCK methods in dav_ext_methods for locking to function.
When locking is enabled, the module responds with DAV: 2 in the OPTIONS response, indicating DAV class 2 compliance (locking support). Without locking, the module reports DAV: 1.
Important: The module supports only exclusive write locks, which is the only lock type defined in the WebDAV specification (RFC 4918). All active locks are stored in a linked list, so lock checking requires O(n) operations. A very large number of simultaneous locks may degrade performance. Therefore, avoid setting an excessively long lock timeout.
Configuration Examples
Basic WebDAV Server (No Locking)
This minimal configuration provides file upload, download, directory listing, and file management without locking support:
server {
listen 80;
server_name files.example.com;
location /webdav/ {
alias /srv/webdav/;
# Standard DAV methods for file operations
dav_methods PUT DELETE MKCOL COPY MOVE;
# Extended DAV methods for directory listing
dav_ext_methods PROPFIND OPTIONS;
# Auto-create intermediate directories on upload
create_full_put_path on;
# Set file permissions for uploaded files
dav_access user:rw group:r all:r;
# Allow large file uploads (adjust as needed)
client_max_body_size 1g;
}
}
With this configuration, clients can list files via PROPFIND, upload files via PUT, create directories via MKCOL, and manage files via COPY, MOVE, and DELETE. However, concurrent editing is not protected by locks, so simultaneous writes to the same file may conflict.
WebDAV Server with Locking
This configuration adds lock support for safe concurrent access. It is the recommended setup for shared file servers where multiple users edit files simultaneously:
http {
dav_ext_lock_zone zone=davlock:10m;
server {
listen 80;
server_name files.example.com;
location /webdav/ {
alias /srv/webdav/;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
dav_ext_lock zone=davlock;
create_full_put_path on;
dav_access user:rw group:r all:r;
client_max_body_size 1g;
}
}
}
With locking enabled, a client can lock a file before editing it. Other clients that attempt to modify the locked file receive a 423 Locked response until the lock is released or expires. The default lock timeout is 60 seconds.
macOS Finder Compatibility
macOS Finder sends MKCOL requests without a trailing slash when creating directories. NGINX’s standard DAV module requires the trailing slash for MKCOL, so you need a rewrite rule to add it. This configuration handles the macOS Finder quirk:
http {
dav_ext_lock_zone zone=davlock:10m;
server {
listen 80;
server_name files.example.com;
location /webdav/ {
alias /srv/webdav/;
# Fix macOS Finder MKCOL without trailing slash
set $x $uri$request_method;
if ($x ~ [^/]MKCOL$) {
rewrite ^(.*)$ $1/;
}
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
dav_ext_lock zone=davlock;
create_full_put_path on;
dav_access user:rw group:r all:r;
client_max_body_size 10g;
}
}
}
The rewrite rule detects MKCOL requests to URIs without a trailing slash and appends one. This allows macOS Finder to create directories without errors.
Testing Your WebDAV Server
After applying the configuration, verify the syntax and restart NGINX:
nginx -t && sudo systemctl restart nginx
Preparing the WebDAV Directory
Create the storage directory and set proper ownership:
sudo mkdir -p /srv/webdav
sudo chown nginx:nginx /srv/webdav
Verifying the Module Is Loaded
The most reliable way to confirm the NGINX WebDAV module is loaded is to add module-specific directives and test the configuration:
nginx -t
If nginx -t passes with dav_ext_methods directives present, the module is loaded and functioning. If you see unknown directive "dav_ext_methods", the module is not loaded — check that the load_module line is present and correct.
Testing OPTIONS
The OPTIONS method reveals which WebDAV methods are available:
curl -s -X OPTIONS http://localhost/webdav/ -D - -o /dev/null
Expected output with locking enabled:
HTTP/1.1 200 OK
DAV: 2
Allow: GET,HEAD,PUT,DELETE,MKCOL,COPY,MOVE,PROPFIND,OPTIONS,LOCK,UNLOCK
The DAV: 2 header confirms that locking is active. Without locking, this would show DAV: 1.
Testing PROPFIND
PROPFIND lists directory contents and file properties. Use Depth: 1 to list the immediate children:
curl -s -X PROPFIND http://localhost/webdav/ -H "Depth: 1"
The response is an XML document listing each file and directory with its properties:
<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>/webdav/</D:href>
<D:propstat>
<D:prop>
<D:displayname>webdav</D:displayname>
<D:getlastmodified>Tue, 03 Mar 2026 15:29:53 GMT</D:getlastmodified>
<D:resourcetype><D:collection/></D:resourcetype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>
The module returns six standard WebDAV properties for each resource: displayname, getcontentlength, getlastmodified, resourcetype, lockdiscovery, and supportedlock.
Testing File Operations
Upload a file, verify it exists, then clean up:
# Upload a file
echo "Hello WebDAV" | curl -s -T - http://localhost/webdav/test.txt -D - -o /dev/null
# Read it back
curl -s http://localhost/webdav/test.txt
# Create a directory
curl -s -X MKCOL http://localhost/webdav/documents/ -D - -o /dev/null
# Copy the file into the directory
curl -s -X COPY http://localhost/webdav/test.txt \
-H "Destination: http://localhost/webdav/documents/test.txt" \
-D - -o /dev/null
# Delete the original
curl -s -X DELETE http://localhost/webdav/test.txt -D - -o /dev/null
Testing Locking
Lock a file, attempt modification without the token, then use the token:
# Lock the file
curl -s -X LOCK http://localhost/webdav/documents/test.txt \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="utf-8" ?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockinfo>'
The response includes a Lock-Token header (e.g., <urn:3b658999>) and an XML body confirming the lock details. Note the token value for subsequent requests.
# Attempt to modify without the token — returns 423 Locked
echo "Unauthorized write" | curl -s -T - http://localhost/webdav/documents/test.txt \
-D - -o /dev/null
# Modify with the correct token — succeeds
echo "Authorized write" | curl -s -T - http://localhost/webdav/documents/test.txt \
-H "If: (<urn:3b658999>)" -D - -o /dev/null
# Release the lock
curl -s -X UNLOCK http://localhost/webdav/documents/test.txt \
-H "Lock-Token: <urn:3b658999>" -D - -o /dev/null
Security Best Practices
A WebDAV server exposes file management over HTTP, so it is critical to secure it properly before deploying to production.
Enable Authentication
Never expose a WebDAV server without authentication. Use HTTP Basic Auth at minimum:
# Install htpasswd utility
sudo dnf install httpd-tools # RHEL-based
sudo apt-get install apache2-utils # Debian/Ubuntu
# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd webdav_user
Add the authentication directives to your location block:
location /webdav/ {
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/.htpasswd;
# ... DAV directives ...
}
For stronger authentication, consider using the NGINX auth hash module for token-based URL authentication, or integrate with an external authentication provider using NGINX’s auth_request directive.
Use HTTPS
WebDAV transmits file contents and credentials over the network. Always use TLS in production:
server {
listen 443 ssl;
server_name files.example.com;
ssl_certificate /etc/letsencrypt/live/files.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/files.example.com/privkey.pem;
location /webdav/ {
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/.htpasswd;
alias /srv/webdav/;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
# ... remaining config ...
}
}
You can enhance your TLS configuration with the NGINX security headers module, which automatically adds security-related response headers.
Restrict Access by IP
If your WebDAV server is only accessed from specific networks, combine authentication with IP restrictions:
location /webdav/ {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/.htpasswd;
# ... DAV directives ...
}
Limit Upload Sizes
Set client_max_body_size to a reasonable value for your use case. A file server might allow 1-10 GB uploads, while a document collaboration server might limit to 100 MB:
client_max_body_size 1g;
SELinux Configuration
On RHEL-based distributions with SELinux enabled (the default on Rocky Linux, AlmaLinux, and RHEL), NGINX is restricted from reading and writing arbitrary directories. You must configure the correct SELinux context for your WebDAV storage directory.
Set the httpd_sys_rw_content_t context to allow NGINX to read and write files:
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/srv/webdav(/.*)?"
sudo restorecon -Rv /srv/webdav/
If NGINX uses a custom client_body_temp_path, apply the same context to that directory:
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/tmp/nginx-dav(/.*)?"
sudo restorecon -Rv /tmp/nginx-dav/
Without the correct SELinux context, PROPFIND requests return 500 Internal Server Error and file uploads fail. The NGINX error log will show Permission denied errors even if the Unix file permissions are correct.
You can verify the current context with:
ls -laZ /srv/webdav/
The output should show httpd_sys_rw_content_t for the directory and its contents.
Performance Considerations
Shared Memory Zone Sizing
The dav_ext_lock_zone allocates shared memory for storing lock information. Each lock entry is small (a few hundred bytes), so 10 MB is sufficient for thousands of simultaneous locks. For most deployments, you will never need to increase this value.
Lock Timeout Tuning
The default lock timeout of 60 seconds works well for interactive editing where clients refresh locks frequently. However, for batch operations or slow connections, a longer timeout reduces the risk of lock expiration during file transfers:
dav_ext_lock_zone zone=davlock:10m timeout=300;
A 5-minute timeout is a reasonable balance between lock durability and resource cleanup. Avoid setting very long timeouts (e.g., hours) because active locks are checked linearly, and a large number of stale locks degrades PROPFIND performance.
Directory Listing Performance
PROPFIND with Depth: 1 lists all files in a directory. For directories containing thousands of files, this operation may become slow because NGINX reads each file’s metadata (size, modification time) from the filesystem. If you expect very large directories, consider organizing files into subdirectories.
Upload Buffering
When receiving large file uploads via PUT, NGINX buffers the request body in the client_body_temp_path directory before writing it to the final location. Ensure this directory has enough disk space and is on the same filesystem as the WebDAV root to allow atomic renames:
client_body_temp_path /srv/webdav/.tmp;
client_max_body_size 10g;
Using the same filesystem avoids a costly copy operation during the final file move.
Troubleshooting
“Unknown directive” Error
If nginx -t reports unknown directive "dav_ext_methods":
- Verify the module file exists:
ls /usr/lib64/nginx/modules/ngx_http_dav_ext_module.so - Ensure the
load_moduledirective is at the top ofnginx.conf, outside any block. - Verify the module package version matches your NGINX version:
rpm -q nginx-module-dav-ext
PROPFIND Returns 500 Internal Server Error
This is almost always a permissions issue. Check the NGINX error log:
tail -f /var/log/nginx/error.log
Common causes:
- SELinux blocking access — The error log shows
Permission deniedeven with correct Unix permissions. Apply thehttpd_sys_rw_content_tcontext as described in the SELinux section above. - Directory not owned by nginx user — Run
chown -R nginx:nginx /srv/webdav/. - Incorrect alias path — Ensure the
aliasdirective path ends with a trailing slash and the directory actually exists.
Locked File Returns 423 but You Lost the Token
If a client crashes without unlocking a file, you must wait for the lock timeout to expire. The default timeout is 60 seconds. For development or testing, you can restart NGINX to clear all locks from shared memory:
sudo systemctl restart nginx
In production, the recommended approach is to set a reasonable timeout value in dav_ext_lock_zone rather than restarting NGINX.
macOS Finder Cannot Create Directories
macOS Finder sends MKCOL requests without a trailing slash. Add the rewrite rule from the macOS Finder Compatibility section. Without this fix, NGINX returns 409 Conflict when Finder tries to create a new folder.
Windows Explorer Shows Empty Folder
Windows Explorer may cache directory listings aggressively. Additionally, Windows requires DAV: 1 or DAV: 2 in the OPTIONS response to recognize a server as WebDAV-capable. Ensure that OPTIONS is listed in your dav_ext_methods directive.
Conclusion
The NGINX WebDAV module completes what the standard DAV module starts. Together, they provide full WebDAV support for file sharing, cloud storage, and collaborative editing directly from NGINX — without needing Apache, Nextcloud, or any additional application server.
For production deployments, always enable HTTPS and authentication, configure SELinux contexts on RHEL-based systems, and choose a lock timeout that matches your workflow. The module’s lightweight design means you can serve WebDAV alongside your existing NGINX configuration with minimal overhead.
The module source code is available on GitHub.

