Repository navigation
HTTPS Setup
Bookoholik runs over plain HTTP by default. HTTPS is an opt-in feature provided by a separate compose overlay (docker-compose.ssl.yml). The base docker-compose.yml is never modified β you simply layer the SSL file on top when you want HTTPS.
| Without HTTPS | With HTTPS |
|---|---|
| Passwords sent in plain text over the network | All traffic encrypted end-to-end |
| JWT tokens visible to anyone on the LAN | Tokens protected in transit |
| Browsers warn "Not Secure" | Green padlock, no warnings |
| Required for some browser features (PWA, clipboard) | Full browser feature support |
Browser (HTTPS)
β 443
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β ssl-proxy (nginx) or caddy β ββ docker-compose.ssl.yml
β TLS termination + reverse proxy β
ββββββββ¬βββββββββββββββββββββββ¬ββββββββββββ
β http://frontend:80 β http://backend:80
βΌ βΌ
ββββββββββββββ ββββββββββββββββββ
β frontend β β backend β ββ docker-compose.yml (base, unchanged)
β (nginx SPA)β β (PHP + Apache) β
ββββββββββββββ ββββββββββββββββββ
The SSL proxy sits in front of both containers. It:
- Redirects HTTP β HTTPS
- Terminates TLS
- Proxies
/api/*βbackend:80 - Proxies everything else β
frontend:80
SSL_MODE |
When to use |
|---|---|
nginx-selfsigned |
Home LAN, no domain. Uses ./docker/ssl-gen.sh to create a self-signed cert. |
nginx-letsencrypt |
You have a domain and already ran certbot. Mount your cert files. |
caddy |
You have a public domain. Caddy obtains & renews Let's Encrypt certs automatically. |
Best for: home use, local network, no public domain.
# Usage: ./docker/ssl-gen.sh [IP] [optional-hostname]
./docker/ssl-gen.sh 192.168.1.12
# β creates ./certs/server.crt and ./certs/server.keyFor multiple SANs (IP + hostname):
./docker/ssl-gen.sh 192.168.1.12 bookoholik.homeOr use mkcert for a trusted local CA (no browser warning on your devices):
brew install mkcert && mkcert -install
mkdir -p certs
mkcert -key-file ./certs/server.key -cert-file ./certs/server.crt \
192.168.1.12 localhost 127.0.0.1cp .env.ssl.example .env.ssl
# Edit .env.ssl β set SSL_DOMAIN to your LAN IP (e.g. 192.168.1.12)CORS_ORIGIN=https://192.168.1.12
API_URL=https://192.168.1.12/api
APP_URL=https://192.168.1.12
NGINX_API_URL=https://192.168.1.12Note: No port is needed in the URL β the SSL proxy handles routing on port 443.
docker compose \
-f docker-compose.yml \
-f docker-compose.ssl.yml \
--env-file .env \
--env-file .env.ssl \
--profile nginx-ssl \
up -d --buildAccess the app at https://192.168.1.12.
Your browser will warn about the self-signed certificate β click Advanced β Proceed.
To eliminate the warning permanently, trust the certificate (see below).
macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ./certs/server.crtiOS / iPadOS:
- Email
./certs/server.crtto yourself, or temporarily host it:python3 -m http.server 9000 --directory certs # β http://192.168.1.12:9000/server.crt - Open the URL on the device β Settings β Profile Downloaded β Install
- Settings β General β About β Certificate Trust Settings β toggle the cert on
Windows:
- Double-click
server.crtβ Install Certificate - Place in Trusted Root Certification Authorities
Follow Option A but use mkcert to generate the certificate. The key advantage: no browser warning on any device where mkcert -install has been run.
brew install mkcert nss && mkcert -install
mkdir -p certs
mkcert -key-file ./certs/server.key -cert-file ./certs/server.crt \
192.168.1.12 bookoholik.home localhost 127.0.0.1Then follow steps 2β4 from Option A.
For iOS / Android with mkcert: copy $(mkcert -CAROOT)/rootCA.pem to the device and install it as a trusted profile.
Best for: a domain you control, certbot already set up on the host.
# Stop any service using port 80 first, then:
certbot certonly --standalone -d bookoholik.yourdomain.com
# Cert path: /etc/letsencrypt/live/bookoholik.yourdomain.com/SSL_MODE=nginx-letsencrypt
SSL_DOMAIN=bookoholik.yourdomain.com
SSL_CERT_FILE=/etc/letsencrypt/live/bookoholik.yourdomain.com/fullchain.pem
SSL_KEY_FILE=/etc/letsencrypt/live/bookoholik.yourdomain.com/privkey.pemCORS_ORIGIN=https://bookoholik.yourdomain.com
API_URL=https://bookoholik.yourdomain.com/api
APP_URL=https://bookoholik.yourdomain.com
NGINX_API_URL=https://bookoholik.yourdomain.comdocker compose \
-f docker-compose.yml \
-f docker-compose.ssl.yml \
--env-file .env \
--env-file .env.ssl \
--profile nginx-ssl \
up -d --buildBest for: a public domain, zero certificate management.
- A real domain pointing to your server's public IP
- Ports 80 and 443 open on your router/firewall
SSL_MODE=caddy
SSL_DOMAIN=bookoholik.yourdomain.comCORS_ORIGIN=https://bookoholik.yourdomain.com
API_URL=https://bookoholik.yourdomain.com/api
APP_URL=https://bookoholik.yourdomain.com
NGINX_API_URL=https://bookoholik.yourdomain.comdocker compose \
-f docker-compose.yml \
-f docker-compose.ssl.yml \
--env-file .env \
--env-file .env.ssl \
--profile caddy \
up -d --buildCaddy automatically obtains and renews the certificate. No manual cert management needed.
# Stop the SSL stack
docker compose -f docker-compose.yml -f docker-compose.ssl.yml down
# Start plain HTTP again
docker compose up -d| File | Purpose |
|---|---|
docker-compose.ssl.yml |
SSL overlay β extends the base compose file |
docker/nginx-ssl.conf |
nginx reverse proxy config (self-signed / certbot modes) |
docker/Caddyfile |
Caddy config (automatic Let's Encrypt mode) |
docker/ssl-gen.sh |
Helper script to generate a self-signed certificate |
.env.ssl.example |
Template for SSL environment variables |
certs/ |
Where generated certificates are stored (git-ignored) |
-
CORS_ORIGINupdated tohttps://...in base.env -
API_URLupdated tohttps://...in base.env -
APP_URLupdated tohttps://...in base.env -
NGINX_API_URLupdated tohttps://...in base.env - Frontend rebuilt (
--buildflag in the compose command) - Certificate trusted on all devices you use
# Check certificate details
curl -kvI https://192.168.1.12 2>&1 | grep -E "subject|issuer|expire|SSL"
# Check HSTS header
curl -sI https://your-address | grep -i "strict-transport"
# Check HTTP β HTTPS redirect
curl -sI http://your-address | grep -i "location"| Problem | Cause | Fix |
|---|---|---|
NET::ERR_CERT_INVALID |
Self-signed cert not trusted | Install cert as trusted root (see Option A) |
| API calls fail (CORS) |
CORS_ORIGIN still has http://
|
Update .env and rebuild frontend |
| Ebook upload fails (413) | Body size limit | Already set to 110 MB in nginx-ssl.conf β check your proxy chain |
| iOS: "certificate not trusted" | Root CA not installed on device | Follow iOS trust instructions in Option A |
| Let's Encrypt fails | Port 80 not reachable | Check router port forwarding and firewall |
SSL_DOMAIN is required error |
Forgot to set SSL_DOMAIN in .env.ssl
|
Set SSL_DOMAIN=your-ip-or-domain
|
| Caddy: certificate not issued | Domain doesn't resolve to server | Ensure DNS A record points to your public IP |
Next: Troubleshooting
Getting Started
Features
- Book Scanner
- Managing Locations
- Managing Books
- E-Book Plugin
- Publishers & Writers
- Lending System
- Reports & Export
User Guide
Administration
Developer
Release Notes