Skip to Content
DeploymentReverse Proxy

Reverse Proxy

For production deployments, place PicPeak behind a reverse proxy for SSL termination and routing.

The routing below applies to the multi-container Compose stack, where the frontend and backend are separate services. The all-in-one image serves everything from one port, so a proxy in front of it needs a single rule pointing at port 3000 — see Example: NAS at Home for a worked setup with Nginx Proxy Manager.

Routing schema

PathServicePortDescription
/api/*Backend3001All API endpoints
/photos/*Backend3001Protected photo files
/thumbnails/*Backend3001Protected thumbnail files
/uploads/*Backend3001Upload files
/* (everything else)Frontend3000React SPA (including /admin/*, /gallery/*)

The /admin/* routes are served by the frontend (React SPA), not the backend. The backend only handles /api/admin/* requests.

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

server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # Backend: API endpoints location /api/ { proxy_pass http://localhost:3001; 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; } # Backend: Protected media files location ~ ^/(photos|thumbnails|uploads)/ { proxy_pass http://localhost:3001; 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; } # Frontend: Everything else (React SPA) location / { proxy_pass http://localhost:3000; 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; } }

Enable the site:

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

For video uploads, increase the body size limit:

client_max_body_size 10G; proxy_read_timeout 3600; proxy_send_timeout 3600;

Client IP, rate limiting and TRUST_PROXY

PicPeak keys its API rate limiter and its login lockout on the client IP. Behind a proxy the backend only sees the real client address when two things hold: the proxy forwards X-Forwarded-For (all configurations above do), and PicPeak trusts that proxy.

Express’ trust proxy is preset to loopback, linklocal, uniquelocal, which covers proxies on Docker networks and private ranges. If your proxy reaches the backend from a public address — a separate proxy host, a cloud load balancer, a tunnel — add it via TRUST_PROXY:

# .env — a CIDR, a comma list, a hop count, or `true` behind a fully controlled chain TRUST_PROXY=loopback, linklocal, uniquelocal, 203.0.113.0/24

Without it every visitor presents as the proxy’s address, shares one rate-limit budget of 300 requests per 15 minutes, and a busy gallery can start returning 429 for everyone at once. Do not set TRUST_PROXY=true unless every hop in front of PicPeak is yours: a trusted-all setting lets any client spoof its address through X-Forwarded-For.

See Environment Variables → Core.

Mixed HTTPS + LAN HTTP access

Some self-hosted setups want two working entry points to the same PicPeak instance:

  • Public HTTPS via the reverse proxy on a domain (https://photos.example.com)
  • Direct HTTP on the LAN for admin access (http://192.168.1.50:3001)

With the default COOKIE_SECURE setting, only one of these works at a time — the Secure cookie flag either blocks LAN HTTP (when true) or weakens HTTPS cookies (when false).

Set COOKIE_SECURE=auto in your .env to make PicPeak decide per request:

# .env COOKIE_SECURE=auto

In this mode the backend reads req.secure from Express. The flag is set to true when the request arrived over HTTPS (either directly or via a trusted proxy forwarding X-Forwarded-Proto: https) and false otherwise. The end result: both public HTTPS login and LAN HTTP login work with correctly-flagged cookies.

The Nginx, Traefik, and Caddy configurations shown above already forward X-Forwarded-Proto correctly. PicPeak’s Express trust proxy is preset to loopback, linklocal, uniquelocal, which covers Docker networks and private-network proxies out of the box.

See Environment Variables → Authentication cookies for the full reference.

Last updated on