Reverse Proxy (Required in Production)
AsterDrive does not include a TLS terminator.
If you plan to expose the site to the public internet, enable WebDAV, or connect an external Office / WOPI service, you must put a reverse proxy in front to handle:
- HTTPS certificates
- HTTPS-related security response headers, while preserving the browser page baseline
Content-Security-Policyreturned by AsterDrive - HTTP to HTTPS redirects
- large file upload body limits
- SSE long-connection timeout and buffering
- WebDAV method / request header passthrough
- frontend static asset cache headers
Do not expose :3000 directly to the public internet.
That is only suitable for temporary local or intranet bootstrap. For production launch, bind AsterDrive to an internal address, then expose 443 externally through Caddy / Nginx / Traefik.
Align These Values Before Launch
- Set
Admin -> System Settings -> Site Configuration -> Public Site URLto a realhttps://origin. Add multiple public domains one by one, for examplehttps://drive.example.com. - Only set static bootstrap option
auth.bootstrap_insecure_cookiestotruetemporarily during plain HTTP first bootstrap. - After switching to HTTPS, remove
auth.bootstrap_insecure_cookiesand confirm runtimeauth_cookie_securehas been restored to enabled. - The home page response headers should include the browser page baseline
Content-Security-Policyreturned by AsterDrive. The proxy must not remove it or overwrite it with an incompatible policy. - Do not rewrite the site's own baseline CSP into a site-wide
sandbox. - The proxy must not block WebDAV methods such as
PROPFIND,MOVE,COPY,LOCK, andUNLOCK. - The proxy must not overwrite
ETag/Cache-Controlreturned by thumbnail endpoints. - The proxy must preserve the real
Hostand correctly pass the public protocol. AsterDrive uses the request Host to perform exact matching against thePublic Site URLlist, then generates sharing, WebDAV, and WOPI URLs for the corresponding domain. - If you want AsterDrive to identify the real client IP, the front reverse proxy must be the hop you control and trust. AsterDrive only reads client IPs from
X-Forwarded-Forwhen the connection source matchesnetwork_trust.trusted_proxies; otherwise, it ignores forwarded headers and continues using the actual connection source. - Headers such as
X-Forwarded-For/Forwardedmust not be treated directly as user identity credentials. They are only meaningful when the request really comes from a proxy, gateway, or Docker intranet hop you configured. Do not put public CIDR ranges, third-party CDNs, or upstreams you do not control intotrusted_proxies. - If you need rate limiting, audit, or session security decisions based on the real client IP, first read the
[network_trust].trusted_proxiesexplanation in Rate Limiting, then synchronize the same proxy addresses / CIDRs into your reverse proxy topology.
This page assumes:
- AsterDrive listens on
127.0.0.1:3000. - The WebDAV prefix uses the default
/webdav. - The domain is
drive.example.com.
If you changed the listen address, domain, or WebDAV prefix, update the corresponding values in the examples below.
Multiple Domain Entries
One instance can have multiple public entries, for example:
https://drive.example.com
https://panel.example.com
https://intranet-drive.example.netIn the admin panel, still only change Public Site URL, with each origin on its own line. The system only accepts exact HTTP(S) origins. Do not include paths, do not write *, and do not enter domains you do not control. The first line is the default fallback origin.
When a request enters through panel.example.com, and that line is already in the list, the system generates WebDAV, sharing, and WOPI URLs in the form https://panel.example.com/.... Unmatched Hosts are not trusted directly; the system falls back to the first line to avoid arbitrary Host header pollution in external links.
Key Path Quick Reference
| Purpose | Path |
|---|---|
| Frontend page / admin panel / sharing page | / |
| API | /api/v1/ |
| SSE storage change stream | /api/v1/auth/events/storage |
| WOPI callbacks | /api/v1/wopi/ |
| WebDAV | /webdav/ |
| Frontend build assets | /assets/ |
| Embedded static assets | /static/ |
| PDF.js assets | /pdfjs/ |
Caddy
Caddy is the simplest option and handles HTTPS plus HTTP to HTTPS redirects out of the box.
drive.example.com {
encode zstd gzip
@frontend_assets path /assets/*
header @frontend_assets Cache-Control "public, max-age=31536000, immutable"
@embedded_static path /static/* /pdfjs/*
header @embedded_static Cache-Control "public, max-age=86400"
reverse_proxy 127.0.0.1:3000 {
# SSE must flush quickly; do not let the proxy buffer events.
flush_interval -1
}
}This configuration already provides:
- automatic HTTPS
- automatic HTTP to HTTPS redirect
- preservation of the baseline CSP returned by AsterDrive for browser pages
- immediate SSE flushing
- whole-site passthrough for WebDAV / WOPI / normal APIs
Notes:
- Caddy does not impose a very small request body limit by default like Nginx often does. If you add your own
request_bodylimit, remember to raise it accordingly. - Thumbnail endpoints return
ETagandmust-revalidatethemselves. Do not rewrite them here into aggressive cache headers.
Nginx
With Nginx, you handle HTTPS, redirects, upload size, and SSE yourself.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name drive.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name drive.example.com;
ssl_certificate /etc/letsencrypt/live/drive.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/drive.example.com/privkey.pem;
# Do not let the proxy truncate large file uploads.
client_max_body_size 0;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
location = /api/v1/auth/events/storage {
proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
proxy_cache off;
add_header X-Accel-Buffering no always;
}
location ^~ /assets/ {
proxy_pass http://127.0.0.1:3000;
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location ^~ /static/ {
proxy_pass http://127.0.0.1:3000;
expires 1d;
add_header Cache-Control "public, max-age=86400" always;
}
location ^~ /pdfjs/ {
proxy_pass http://127.0.0.1:3000;
expires 1d;
add_header Cache-Control "public, max-age=86400" always;
}
location / {
proxy_pass http://127.0.0.1:3000;
}
}The easiest things to miss in this config are:
client_max_body_size 0proxy_request_buffering off- disabling
proxy_bufferingspecifically for SSE - preserving
X-Forwarded-Protoashttps X-Real-IPis only a helper header; AsterDrive does not use it as a security decision sourceX-Forwarded-Foris only read by the application when the request source matchestrusted_proxies- if another CDN, L4 load balancer, or cloud gateway sits in front of the reverse proxy, decide which hop is the "last proxy layer" trusted by AsterDrive, then put only that hop's IP / CIDR into
trusted_proxies - without
trusted_proxies, AsterDrive uses the actual connection source directly and does not trust client IPs from forwarded headers
If you create a separate location for /webdav/, do not add limit_except to restrict methods. Otherwise clients such as Finder, Windows, and rclone may not work correctly with WebDAV.
Traefik
Traefik is better suited to Docker / Compose scenarios.
It has two parts:
- Traefik's static configuration: entrypoints, HTTPS, and timeouts
- AsterDrive container labels: Host routing and forwarding port
traefik.yml
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 0s
writeTimeout: 0s
idleTimeout: 3600s
providers:
docker:
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
email: ops@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: webSettings such as readTimeout: 0s are important.
Otherwise, large uploads and SSE can time out at the proxy layer first.
docker-compose.yml Labels
services:
asterdrive:
image: ghcr.io/apts-1547/asterdrive:latest
labels:
- traefik.enable=true
- traefik.http.routers.asterdrive.rule=Host(`drive.example.com`)
- traefik.http.routers.asterdrive.entrypoints=websecure
- traefik.http.routers.asterdrive.tls=true
- traefik.http.routers.asterdrive.tls.certresolver=letsencrypt
- traefik.http.routers.asterdrive.service=asterdrive
- traefik.http.routers.asterdrive-assets.rule=Host(`drive.example.com`) && (PathPrefix(`/assets/`) || PathPrefix(`/static/`) || PathPrefix(`/pdfjs/`))
- traefik.http.routers.asterdrive-assets.entrypoints=websecure
- traefik.http.routers.asterdrive-assets.tls=true
- traefik.http.routers.asterdrive-assets.tls.certresolver=letsencrypt
- traefik.http.routers.asterdrive-assets.priority=100
- traefik.http.routers.asterdrive-assets.middlewares=asterdrive-static-cache
- traefik.http.routers.asterdrive-assets.service=asterdrive
- traefik.http.middlewares.asterdrive-static-cache.headers.customresponseheaders.Cache-Control=public, max-age=86400
- traefik.http.services.asterdrive.loadbalancer.server.port=3000Traefik fills common X-Forwarded-* headers by default.
What you need to watch:
webmust redirect towebsecurewebsecuretimeouts must not be too short- do not use a headers middleware to overwrite the page CSP returned by AsterDrive
- do not wrap WebDAV or thumbnail routes in another middleware that overwrites response headers
If you want /assets/ to use more aggressive immutable caching, split out a separate router. Avoid applying strong caching to all /api/v1/*, because that may cache dynamic API responses and cause problems.
CSP / Security Response Headers
AsterDrive now automatically returns a browser page baseline Content-Security-Policy for frontend HTML. The reverse proxy should preserve it, not casually overwrite it with a narrower policy. If a security scanner still reports "no CSP", first check whether the proxy removed the upstream response header, or whether the scan only hit static asset / API paths.
Separate the Two CSP Types First
Production now has two different policy layers that must be kept distinct:
- Site page baseline CSP: used for HTML pages such as
/, admin panel, and sharing pages. It mainly restricts script, style, image, iframe, worker, and other resource loading sources. - Dangerous file inline sandbox CSP: used for raw inline responses of files with script capability. The backend currently adds
Content-Security-Policy: sandboxonly to responses such astext/html,application/xhtml+xml, andimage/svg+xml.
sandbox itself is a Document directive. It is suitable for restricting document contexts, not a general solution for "turn every file into sandbox".
If you rewrite the site's own baseline CSP into a site-wide sandbox, normal HTML such as the login page, admin panel, and sharing pages will also enter a sandbox. Scripts, forms, storage, and same-origin capabilities become unavailable, breaking core site functionality.
Deployment should follow these principles:
- The reverse proxy preserves the baseline CSP returned by AsterDrive for site pages.
- Do not rewrite the whole site's
Content-Security-Policytosandbox. - Do not remove the upstream
Content-Security-Policyreturned for dangerous inline files at the proxy layer. - If both proxy layer and application layer return CSP, browsers enforce both policies. This is allowed, but the effective result is the stricter combination.
The current page baseline policy is:
default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: http: https:;
font-src 'self' data:;
connect-src 'self' http: https: ws: wss:;
media-src 'self' blob:;
worker-src 'self' blob:;
frame-src 'self' http: https:;
manifest-src 'self';This policy is based on current frontend behavior. Confirm impact before changing it:
script-src 'unsafe-inline'must remain for now; custom frontends and placeholder injection may still include inline scripts.style-src 'unsafe-inline'must remain for now; the frontend uses runtime inline styles and dynamic<style>, and removing it may break styling.img-src 'self' data: blob: http: https:andmedia-src 'self' blob:must remain; thumbnails, image / video preview, avatar cropping, and external-link icons use these sources.connect-src 'self' http: https: ws: wss:must remain; presigned upload / download, remote followers, and realtime push use these.worker-src 'self' blob:must remain; PDF preview uses workers, and some build styles use blob workers.frame-src 'self' http: https:must remain; external preview applications and WOPI entries may use cross-origin iframes.
If you must overwrite CSP at the gateway layer, start by copying the policy above, then tighten it according to your real deployment. Do not directly apply a generic template that only allows connect-src 'self'; presigned upload, external preview, or follower nodes can easily be blocked by that.
When tightening further, first run Content-Security-Policy-Report-Only through a real acceptance pass, then switch to enforcement mode.
At minimum, test login, upload, PDF preview, text preview, sharing page, avatar, and external preview application / WOPI once.
Do Not Miss These When Proxying WebDAV
If the proxy is whole-site passthrough, WebDAV is usually fine.
Problems usually come from manually added restrictions:
- restricting
PROPFIND,LOCK, orUNLOCK - removing headers such as
AuthorizationorDestination - changing
/webdav/to another prefix without updating client addresses too
If you change [webdav].prefix = "/dav", update both the proxy layer and client addresses to /dav/.
Extra Requirements for WOPI / Office Callbacks
If you integrate OnlyOffice, Collabora, or another WOPI service, confirm two more things:
public_site_urlmust include a real HTTPS origin that the WOPI host can connect back to. If there are multiple entries, add them one by one toPublic Site URL.- The external Office service must be able to access
https://your-domain/api/v1/wopi/....
The most common symptoms are:
- opener buttons are visible, but loading fails after clicking
- the Office page opens, but cannot read the file
- the file opens, but cannot save back to AsterDrive
Preserve Revalidation Semantics for Thumbnail Cache
AsterDrive thumbnail endpoints already return:
ETagCache-Control: public/private, max-age=0, must-revalidate
So the proxy layer should:
- preserve these response headers
- allow the browser to use
If-None-Matchfor 304 revalidation
Do not:
- rewrite thumbnails to
immutable - remove
ETag - force CDN caching for hours without revalidation
Minimum Post-Launch Acceptance
- The browser can log in normally through
https://your-domain/. - The home page response headers include the browser page baseline
Content-Security-Policyreturned by AsterDrive, and the proxy has not removed it or overwritten it with an incompatible policy. - Every line in
Admin -> System Settings -> Public Site URLis a realhttps://origin. If there are multiple public domains, log in through each one and confirm WebDAV addresses and sharing links use the current domain. - Upload a large file and confirm it is not truncated by the proxy.
- Open two browser tabs and confirm file changes refresh through SSE.
- If WebDAV is enabled, connect once with a real client.
- If WOPI is enabled, open and save a real Office file once.