====== BookWyrm with Cloudflare Tunnel ====== This guide covers running BookWyrm using the official docker-compose from the BookWyrm repository, accessed via a Cloudflare Tunnel. The official compose already includes nginx and certbot. For a Cloudflare Tunnel setup, certbot is unnecessary since Cloudflare handles TLS at the edge. See [[hosting:docker:bookwyrm_caddy|BookWyrm with Caddy]] if you are integrating BookWyrm into your own existing compose stack instead. ===== Traffic flow ===== ``` Cloudflare -- HTTPS --> cloudflared -- HTTP --> nginx:80 --> /static/ served directly --> everything else --> web:8000 (Gunicorn) ``` The most common mistake is pointing the tunnel directly at ''web:8000'' (Gunicorn), which bypasses nginx entirely. Gunicorn returns HTML for every ''/static/'' request, and the browser blocks it due to MIME type mismatch. No styles load. ===== Setup ===== ==== 1. Clone the BookWyrm repo ==== ```bash git clone https://github.com/bookwyrm-social/bookwyrm.git cd bookwyrm git checkout production cp .env.example .env ``` Edit ''.env'' and fill in your domain, passwords, and email settings. ==== 2. Switch nginx to reverse proxy mode ==== In your ''.env'', add: ``` NGINX_SETUP=reverse_proxy ``` The BookWyrm ''nginx/'' directory includes a ''reverse_proxy.conf'' specifically for setups where TLS is handled upstream. This makes nginx listen on port 80 only, with no SSL or certbot involvement. **Do not use ''NGINX_SETUP=http''.** There is no ''http.conf'' in the repo. If Docker cannot find the config file when the container starts, it creates a directory at that path instead, and nginx falls back to its default welcome page. ==== 3. Set USE_HTTPS to true ==== Even though the server speaks plain HTTP internally, BookWyrm needs to know it is behind HTTPS so it generates correct ''https://'' URLs: ``` USE_HTTPS=true ``` ==== 4. Point your Cloudflare tunnel at nginx ==== In your ''cloudflared'' config (''config.yml''): ```yaml ingress: - hostname: books.yourdomain.com service: http://nginx:80 - service: http_status:404 ``` If you manage your tunnel through the Cloudflare dashboard, go to **Zero Trust -> Networks -> Tunnels**, edit your tunnel, and set the service URL to ''http://nginx:80''. If cloudflared is running on the host rather than inside Docker, use ''http://localhost:'' instead. ==== 5. Start the stack ==== Always use a full ''down'' followed by ''up'' when changing config. If you only run ''up -d'', Docker may keep stale container mounts from a previous run: ```bash docker compose down && docker compose up -d ``` ===== Anubis (bot protection) ===== The official BookWyrm compose includes [[https://github.com/TecharoHQ/anubis|Anubis]], a proof-of-work bot protection layer. nginx routes all requests through Anubis before passing them to Gunicorn. If you comment out Anubis, you must also fix the ''nginx/locations'' file. The default ''locations'' file has ''auth_request'' and ''proxy_pass'' inside ''location /'' tied to Anubis. Commenting out only Anubis leaves an empty or broken ''location /'' block, causing 502 on every request. ==== Running without Anubis ==== Replace the contents of ''nginx/locations'' with a version that removes all Anubis references: ```nginx http2 on; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; gzip on; gzip_disable "msie6"; proxy_read_timeout 1800s; chunked_transfer_encoding on; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header Host $host; proxy_redirect off; absolute_redirect off; location ~ ^/(login[^-/]|password-reset|resend-link|2fa-check) { limit_req zone=loginlimit burst=1 nodelay; limit_req_status 429; proxy_pass http://web; } location /api/updates/ { access_log off; proxy_pass http://web; } location / { proxy_pass http://web; } location /static/ { root /app; try_files $uri =404; add_header X-Cache-Status STATIC; access_log off; } location /images/ { location ~ \.(bmp|ico|jpg|jpeg|png|svg|tif|tiff|webp)$ { root /app; try_files $uri =404; add_header X-Cache-Status STATIC; access_log off; } return 403; } location /flower/ { proxy_pass http://flower; proxy_cache off; } ``` The file is called ''locations'' with no extension, inside the ''nginx/'' directory next to ''docker-compose.yml''. ===== Troubleshooting ===== ==== nginx shows its default welcome page ==== The ''NGINX_SETUP'' value does not match a file in ''nginx/''. Check that ''nginx/reverse_proxy.conf'' exists. Then run ''docker compose down && docker compose up -d'' (not just ''up -d'') to clear any bad bind mounts Docker created. ==== 502 Bad Gateway ==== nginx is running but cannot reach the upstream. The most likely cause is a broken ''nginx/locations'' file after commenting out Anubis. Check that ''location /'' has an active ''proxy_pass http://web;'' line (not commented out). Then restart: ```bash docker compose down && docker compose up -d ``` ==== CSS and JS still not loading after pointing tunnel at nginx ==== Confirm the tunnel is actually reaching nginx and not bypassing it. Check the tunnel service URL in your Cloudflare dashboard or ''config.yml'' and verify it says ''http://nginx:80'', not ''http://web:8000''. ===== See Also ===== * [[hosting:docker:bookwyrm|BookWyrm overview]] * [[hosting:docker:bookwyrm_caddy|BookWyrm with Caddy]] Last updated: 2026-06-21 * [[hosting:docker:start|Return to Docker]]