Skip to content

Nginx configuration

nginx decides what to serve itself (reads) and what to proxy to the API (writes, listings).

Routing

One location, split purely by method:

location / {
    # PUT, DELETE, QUERY → proxy to API (raw $request_uri preserves percent-encoding)
    limit_except GET HEAD {
        proxy_pass http://api$request_uri;
    }
    # GET, HEAD → serve directly from disk
    root /data;
    try_files $uri =404;
}
  • limit_except GET HEAD proxies every other method: PUT, DELETE and the QUERY listing. QUERY is never named; it proxies by not being GET/HEAD, so no URI-shape rule is needed.
  • GET and HEAD fall through to try_files, served from disk via sendfile; Python is never on the read path. A GET on a trailing-slash path is not a file and answers 404 here (the bare API would answer 405 + Allow: QUERY).

Forward $request_uri, not $uri

$uri is the normalised, percent-decoded form; forwarded, a path with spaces or unicode makes a malformed request line and the upstream answers 400. Forward the raw $request_uri. Auth still matches on $uri, so .. and %2F.. variants resolve to one canonical form, and safe_path on the API side rejects any .. segment that survives.

Minimal working config

worker_processes auto;
events {}
http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    access_log off;

    upstream api {
        server unix:/run/putfs/putfs.sock;
    }

    server {
        listen 80;

        location / {
            limit_except GET HEAD {
                proxy_pass http://api$request_uri;
            }
            root /data;
            try_files $uri =404;
        }
    }
}

Unauthenticated and unhardened. For production, add:

Concern Where
Authentication Auth: map-based key + secret with path/method scopes, or auth_request
Stored-XSS hardening Static file serving: Content-Disposition: attachment, nosniff, CSP, type reset. Mandatory unless storage runs on its own origin
Upload size limits client_max_body_size 0; in the server block (default 1MB)
Upload streaming proxy_request_buffering off; avoids double disk I/O
Rate limiting limit_req_zone, see Nginx tuning
Timeouts client_body_timeout 300s; proxy_read_timeout 300s; for large files
Performance Nginx tuning: keepalive, gzip, high concurrency

The shipped contrib/putfs.nginx.conf has all of it wired.