All source listed below is under MIT license if no LICENSE file stating different is available.

WebDAV Server with aiohttp

A comprehensive, production-ready WebDAV server implementation with full RFC 4918 compliance, Windows Explorer compatibility, and enterprise-grade features.

Features

Core WebDAV Protocol

  • Full RFC 4918 Compliance: All standard WebDAV methods implemented
  • Windows Explorer Integration: Seamless compatibility with Windows WebDAV Mini-Redirector
  • Multiple Authentication Methods: Basic, Digest, and Token-based authentication
  • Resource Locking: Exclusive and shared locks with timeout management
  • Custom Properties: Full property management (PROPFIND/PROPPATCH)
  • Collection Operations: MKCOL, COPY, MOVE with depth support

Enterprise Features

  • 🔒 Multi-User Support: Isolated user directories with permissions
  • 💾 SQLite Database: Robust backend for users, locks, and properties
  • 🌐 Web Management Interface: Admin dashboard and user portal
  • 🚀 High Performance: Async I/O with aiohttp and aiofiles
  • 🔐 Security Focused: Input validation, path traversal prevention, SQL injection protection
  • 📊 Monitoring Ready: Structured logging and metrics collection
  • 🐳 Docker Support: Container-ready with docker-compose
  • 📈 Production Ready: Gunicorn integration, health checks, graceful shutdown

Quick Start

Prerequisites

  • Python 3.8 or higher
  • pip (Python package manager)
  • Optional: Redis for caching
  • Optional: Docker for containerized deployment

Installation

  1. Clone or create the project directory:

    mkdir webdav-server
    cd webdav-server
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Configure environment:

    cp .env.example .env
    # Edit .env with your configuration
    nano .env
    
  4. Generate a secure secret key:

    python -c "import secrets; print(secrets.token_hex(32))"
    # Copy the output to JWT_SECRET_KEY in .env
    
  5. Run the server:

    python main.py
    

The server will start on http://0.0.0.0:8080 by default.

First Run

On first run, the server automatically creates:

  • A default admin user: admin / admin123
  • The WebDAV root directory structure
  • SQLite database with all required tables

⚠️ Important: Change the default admin password immediately!

Configuration

Environment Variables

All configuration is done through the .env file. Key settings:

Server Configuration

HOST=0.0.0.0              # Listen address
PORT=8080                 # Listen port
SSL_ENABLED=false         # Enable HTTPS

Authentication

AUTH_METHODS=basic,digest # Supported auth methods
JWT_SECRET_KEY=...        # Secret for sessions (required)
SESSION_TIMEOUT=3600      # Session duration in seconds

WebDAV Settings

WEBDAV_ROOT=./webdav         # Root directory for files
MAX_FILE_SIZE=104857600      # Max file size (100MB)
MAX_PROPFIND_DEPTH=3         # Depth limit for PROPFIND
LOCK_TIMEOUT_DEFAULT=3600    # Default lock timeout

See .env.example for complete configuration options.

Usage

Connecting with Windows Explorer

Method 1: Map Network Drive

  1. Open File Explorer
  2. Right-click "This PC" → "Map network drive"
  3. Choose a drive letter
  4. Enter the WebDAV URL:
    http://your-server:8080/
    
  5. Check "Connect using different credentials"
  6. Enter your username and password
  7. Click "Finish"

Method 2: Add Network Location

  1. Open File Explorer
  2. Right-click "This PC" → "Add a network location"
  3. Choose "Choose a custom network location"
  4. Enter the WebDAV URL:
    http://your-server:8080/
    
  5. Enter credentials when prompted

For HTTPS connections:

https://your-server:8443/

Note: Windows requires port 443 for HTTPS WebDAV by default. To use other ports, modify Windows registry:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
BasicAuthLevel = 2

Connecting with macOS Finder

  1. Open Finder
  2. Press Cmd+K (Go → Connect to Server)
  3. Enter the WebDAV URL:
    http://your-server:8080/
    
  4. Click "Connect"
  5. Enter your credentials

Connecting with Linux (davfs2)

  1. Install davfs2:

    sudo apt-get install davfs2  # Ubuntu/Debian
    
  2. Mount the WebDAV share:

    sudo mount -t davfs http://your-server:8080/ /mnt/webdav
    
  3. Enter credentials when prompted

Command Line Tools

Using curl

Upload a file:

curl -u username:password -T file.txt http://localhost:8080/file.txt

Download a file:

curl -u username:password http://localhost:8080/file.txt -o file.txt

Create a directory:

curl -u username:password -X MKCOL http://localhost:8080/newdir/

Delete a resource:

curl -u username:password -X DELETE http://localhost:8080/file.txt

Using cadaver

cadaver http://localhost:8080/
# Enter credentials
dav:/> ls
dav:/> put localfile.txt
dav:/> get remotefile.txt
dav:/> mkcol newfolder

User Management

Creating Users Programmatically

import asyncio
from main import Database

async def create_user():
    db = Database('./webdav.db')
    user_id = await db.create_user(
        username='john',
        password='SecurePass123!',
        email='john@example.com',
        role='user'
    )
    print(f"Created user with ID: {user_id}")

asyncio.run(create_user())

User Roles

  • admin: Full access to all features and user management
  • user: Standard user with access to their own directory
  • readonly: Read-only access (future implementation)

Directory Structure

webdav/
├── users/
│   ├── admin/          # Admin user directory
│   ├── john/           # John's private directory
│   └── jane/           # Jane's private directory
├── shared/             # Shared between all users (optional)
└── public/             # Public access (optional)

Production Deployment

Using Gunicorn

Create gunicorn_config.py:

import multiprocessing

bind = "0.0.0.0:8080"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "aiohttp.GunicornWebWorker"
keepalive = 30
timeout = 60
accesslog = "./logs/access.log"
errorlog = "./logs/error.log"
loglevel = "info"

Run with Gunicorn:

gunicorn main:init_app --config gunicorn_config.py

Using Docker

Create Dockerfile:

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Create directories
RUN mkdir -p /app/webdav /app/logs

# Expose port
EXPOSE 8080

# Run application
CMD ["python", "main.py"]

Create docker-compose.yml:

version: '3.8'

services:
  webdav:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ./webdav:/app/webdav
      - ./logs:/app/logs
      - ./webdav.db:/app/webdav.db
    environment:
      - HOST=0.0.0.0
      - PORT=8080
      - DB_PATH=/app/webdav.db
      - WEBDAV_ROOT=/app/webdav
    restart: unless-stopped

  # Optional: Redis for caching
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    restart: unless-stopped

Build and run:

docker-compose up -d

Nginx Reverse Proxy

Create /etc/nginx/sites-available/webdav:

server {
    listen 80;
    server_name webdav.example.com;

    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name webdav.example.com;

    ssl_certificate /etc/letsencrypt/live/webdav.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webdav.example.com/privkey.pem;

    # WebDAV specific settings
    client_max_body_size 100M;
    client_body_buffer_size 128k;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebDAV headers
        proxy_set_header Destination $http_destination;
        proxy_set_header Depth $http_depth;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Enable and reload:

sudo ln -s /etc/nginx/sites-available/webdav /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Security Best Practices

SSL/TLS Configuration

  1. Always use HTTPS in production
  2. Generate SSL certificates with Let's Encrypt:
    sudo certbot certonly --nginx -d webdav.example.com
    
  3. Update .env:
    SSL_ENABLED=true
    SSL_CERT_PATH=/etc/letsencrypt/live/webdav.example.com/fullchain.pem
    SSL_KEY_PATH=/etc/letsencrypt/live/webdav.example.com/privkey.pem
    

Authentication

  • Use Digest authentication over Basic when possible
  • Enforce strong passwords (min length, complexity)
  • Enable rate limiting to prevent brute force attacks
  • Implement account lockout after failed attempts

Firewall Rules

# Allow only necessary ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Regular Updates

# Update dependencies
pip install --upgrade -r requirements.txt

# Backup database before updates
cp webdav.db webdav.db.backup

Monitoring and Logging

Log Files

Logs are stored in ./logs/ directory:

  • webdav.log - Application logs
  • access.log - Access logs (requests)
  • error.log - Error logs

Log Format

JSON-structured logs for easy parsing:

{
  "timestamp": "2025-01-15T10:30:45Z",
  "level": "INFO",
  "user": "john",
  "method": "PROPFIND",
  "path": "/documents/",
  "status": 207,
  "duration_ms": 45
}

Health Check

Access the health check endpoint:

curl http://localhost:8080/health

Troubleshooting

Windows Explorer Connection Issues

Problem: "The network folder is no longer available"

Solutions:

  1. Increase Windows WebClient timeout:

    Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    FileSizeLimitInBytes = DWORD: 0xFFFFFFFF
    
  2. Enable Basic Authentication over HTTP (insecure - use only for testing):

    Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    BasicAuthLevel = 2
    
  3. Restart WebClient service:

    net stop webclient
    net start webclient
    

Problem: "The specified server cannot perform the requested operation"

Solution: Ensure the URL ends with a slash and doesn't include port 80:

  • http://server:80/path
  • http://server/path/

macOS Finder Issues

Problem: Connection refused or timeout

Solutions:

  1. Use HTTP URL with explicit protocol:

    http://server:8080/
    
  2. Check firewall settings on server

  3. Try connecting via IP address instead of hostname

Performance Issues

Problem: Slow PROPFIND responses

Solutions:

  1. Reduce MAX_PROPFIND_DEPTH in .env:

    MAX_PROPFIND_DEPTH=1
    
  2. Enable caching with Redis:

    CACHE_ENABLED=true
    REDIS_HOST=localhost
    
  3. Increase worker processes:

    WORKERS=8
    

Database Locked Errors

Problem: "database is locked" errors

Solutions:

  1. Enable WAL mode (automatic in code)
  2. Ensure only one process accesses the database
  3. Use separate databases for multiple instances

API Documentation

WebDAV Methods

PROPFIND - Property Discovery

PROPFIND /documents/ HTTP/1.1
Depth: 1
Content-Type: application/xml

<?xml version="1.0"?>
<D:propfind xmlns:D="DAV:">
    <D:allprop/>
</D:propfind>

PROPPATCH - Property Modification

PROPPATCH /file.txt HTTP/1.1
Content-Type: application/xml

<?xml version="1.0"?>
<D:propertyupdate xmlns:D="DAV:">
    <D:set>
        <D:prop>
            <D:displayname>My Document</D:displayname>
        </D:prop>
    </D:set>
</D:propertyupdate>

LOCK - Resource Locking

LOCK /file.txt HTTP/1.1
Timeout: Second-3600
Content-Type: application/xml

<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    <D:owner>
        <D:href>mailto:john@example.com</D:href>
    </D:owner>
</D:lockinfo>

Development

Running Tests

# Install test dependencies
pip install pytest pytest-asyncio pytest-aiohttp pytest-cov

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=. --cov-report=html

Code Style

# Format code
black main.py

# Lint code
flake8 main.py

# Type checking
mypy main.py

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

MIT License - See LICENSE file for details

Support

For issues, questions, or contributions:

  • GitHub Issues: [Create an issue]
  • Documentation: [Wiki]
  • Community: [Discussions]

Changelog

Version 1.0.0 (2025-01-15)

  • Initial release
  • Full RFC 4918 compliance
  • Windows Explorer compatibility
  • Multi-user support with SQLite backend
  • Basic and Digest authentication
  • Resource locking
  • Custom properties
  • Production-ready with Gunicorn support

Acknowledgments

  • RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
  • aiohttp - Async HTTP client/server framework
  • Python community

Made with ❤️ for the WebDAV community

nginx
.env.example
.gitignore
conftest.py
docker-compose.yml
Dockerfile
gunicorn_config.py
main.py
pytest.ini
QUICKSTART.md
README.md
requirements.txt
setup.sh
test_webdav.py
webdav_cli.py
webdav.service