Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🔒 Reverse Proxy & HTTPS Setup

This guide explains how to deploy MyAstroBoard behind a reverse proxy (like NGINX Proxy Manager, Traefik, or Apache) with HTTPS termination.

Overview

In a typical production deployment:

  • Reverse Proxy (NGINX Proxy Manager, Traefik, etc.) handles HTTPS/SSL on port 443
  • MyAstroBoard runs in Docker on HTTP (e.g., port 5000)
  • The reverse proxy forwards requests to MyAstroBoard via HTTP internally

The Challenge

When the HTTPS-only session cookie setting is on, Flask sets the Secure flag on session cookies, which instructs browsers to only send the cookie over HTTPS connections. However, if Flask doesn’t know the original request was HTTPS (because it only sees HTTP from the proxy), cookies and sessions may not work correctly.

The Solution: ProxyFix Middleware

MyAstroBoard includes support for the ProxyFix middleware, which allows Flask to trust proxy headers sent by the reverse proxy. This is required for proper HTTPS operation.


Configuration

Step 1: Enable Proxy Settings in the Admin UI

After deploying, log in as admin and go to App settings -> Configuration -> Reverse proxy:

  • ✅ Trust proxy headers (X-Forwarded-*) - required for Flask to see the real HTTPS scheme
  • ✅ HTTPS-only session cookie (Secure flag) - prevents cookie transmission over HTTP

Click Save, then Restart container when prompted.

These settings are stored in the database (data/myastroboard.db) and persist across container rebuilds.

Step 2: Configure Your Reverse Proxy

Your reverse proxy must send the following headers to MyAstroBoard:

Required Headers:

  • X-Forwarded-Proto: https (tells Flask the original request was HTTPS)
  • X-Forwarded-For: <client-ip> (preserves client IP for logging)
  • X-Forwarded-Host: <your-domain> (original hostname)
  • X-Forwarded-Port: 443 (original port)

Reverse Proxy Examples

NGINX Proxy Manager (NPM)

NPM Configuration

  1. Create Proxy Host in NGINX Proxy Manager:
    • Domain Names: myastroboard.yourdomain.com
    • Scheme: http
    • Forward Hostname/IP: Your Docker host IP or container name
    • Forward Port: 5000
  • Cache Assets: ✅ Enabled (recommended)
  • Block Common Exploits: ✅ Enabled
  • Websockets Support: ✅ Enabled (if using any websocket features)
  1. SSL Tab:
    • SSL Certificate: Select or create Let’s Encrypt certificate
  • Force SSL: ✅ Enabled
  • HTTP/2 Support: ✅ Enabled
  • HSTS Enabled: ✅ Enabled (recommended)
  1. Advanced Tab (add this):
# Proxy headers for Flask ProxyFix
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Prefix /;

# Standard proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

# Timeouts (adjust if needed)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

Docker Compose for NPM + MyAstroBoard

If running NPM and MyAstroBoard on the same Docker host:

version: '3.8'

services:
  nginx-proxy-manager:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    ports:
      - "80:80"
      - "443:443"
      - "81:81"  # NPM Admin UI
    volumes:
      - ./npm/data:/data
      - ./npm/letsencrypt:/etc/letsencrypt
    restart: unless-stopped
    networks:
      - proxy-network

  myastroboard:
    image: myastroboard/myastroboard:latest
    container_name: myastroboard
    environment:
      - TZ=UTC
      - DATA_DIR=/app/data
    # Don't expose port 5000 externally - NPM will proxy to it
    # After first login: App settings -> Configuration -> Reverse proxy -> enable both options -> Restart
    expose:
      - "5000"
    volumes:
      - ./myastroboard/data:/app/data
    restart: unless-stopped
    networks:
      - proxy-network

networks:
  proxy-network:
    driver: bridge

In NPM, use myastroboard as the Forward Hostname (Docker service name) and 5000 as the port.


Traefik

docker-compose.yml with Traefik

version: '3.8'

services:
  myastroboard:
    image: myastroboard/myastroboard:latest
    container_name: myastroboard
    environment:
      - TZ=UTC
      - DATA_DIR=/app/data
    # After first login: App settings -> Configuration -> Reverse proxy -> enable both options -> Restart
    expose:
      - "5000"
    volumes:
      - ./data:/app/data
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myastroboard.rule=Host(`myastroboard.yourdomain.com`)"
      - "traefik.http.routers.myastroboard.entrypoints=websecure"
      - "traefik.http.routers.myastroboard.tls=true"
      - "traefik.http.routers.myastroboard.tls.certresolver=letsencrypt"
      - "traefik.http.services.myastroboard.loadbalancer.server.port=5000"
    networks:
      - traefik-network

networks:
  traefik-network:
    external: true

Traefik automatically adds the required X-Forwarded-* headers.


Standard NGINX

If using standalone NGINX (not NPM):

server {
    listen 443 ssl http2;
    server_name myastroboard.yourdomain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:5000;
        
        # Required headers for ProxyFix
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        
        # Standard headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # WebSocket support (if needed)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name myastroboard.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

Apache

<VirtualHost *:443>
    ServerName myastroboard.yourdomain.com
    
    SSLEngine on
    SSLCertificateFile /path/to/cert.pem
    SSLCertificateKeyFile /path/to/key.pem
    
    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
    
    ProxyPass / http://localhost:5000/
    ProxyPassReverse / http://localhost:5000/
</VirtualHost>

<VirtualHost *:80>
    ServerName myastroboard.yourdomain.com
    Redirect permanent / https://myastroboard.yourdomain.com/
</VirtualHost>

Serving under a sub-path

MyAstroBoard can also live under a path, e.g. https://example.org/myastroboard/. The proxy strips the path before forwarding and sends it in X-Forwarded-Prefix; with Trust proxy headers enabled, every page, API call and asset URL is then built under that prefix.

location /myastroboard/ {
    proxy_pass http://myastroboard:5000/;
    proxy_set_header X-Forwarded-Prefix /myastroboard;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-Host   $host;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
}

Limitations under a sub-path: no installable app (PWA), no offline page and no notifications - the service worker is root-scoped (see NOTIFICATIONS.md). Serve MyAstroBoard from its own host name if you need them.

The Home Assistant app uses the same mechanism for its sidebar panel (ingress, via the MYASTROBOARD_HA_INGRESS variable set by the app - never set it on a plain Docker install).

External base URL

Some addresses are opened by other software rather than by your browser: the Astrodex stream (Home Assistant camera, Lovelace card) and the address MyAstroShine sends photos back to. By default they are built from the address you are browsing on. Set App settings -> Configuration -> Reverse proxy -> External base URL when that address is not reachable by that software - it is required when you use MyAstroBoard from the Home Assistant sidebar.


Settings Reference

These used to be environment variables; since they moved to the admin UI, the variables TRUST_PROXY_HEADERS, SESSION_COOKIE_SECURE and SECRET_KEY are ignored and can be removed from docker-compose.yml.

Setting (App settings -> Configuration -> Reverse proxy)DefaultDescription
Trust proxy headers (X-Forwarded-*)offEnable the ProxyFix middleware so Flask sees the client’s address and the HTTPS scheme. Turn on only behind a trusted reverse proxy. Needs a restart
HTTPS-only session cookieoffSet the Secure flag on the session cookie. Refused from a plain-HTTP page
External base URLemptyAddress other software reaches this instance at (see above)

The session signing key is generated on first start and kept in the database: sessions survive restarts and updates without any setting. The session cookie is always HttpOnly and SameSite=Lax.


Testing Your Setup

1. Verify HTTPS is Working

curl -I https://myastroboard.yourdomain.com

Should return HTTP/2 200 or HTTP/1.1 200 OK

2. Check Secure Cookies

In browser DevTools (Network tab), inspect the response headers for /api/auth/login:

  • Look for Set-Cookie header
  • Should include Secure; HttpOnly; SameSite=Lax

3. Test Remember Me Feature

  1. Login with “Remember me” checked
  2. Close browser completely
  3. Reopen and navigate to your MyAstroBoard URL
  4. Should automatically be logged in

4. Check Logs

In MyAstroBoard logs:

docker logs myastroboard

You should see:

ProxyFix middleware enabled - trusting X-Forwarded-* headers from reverse proxy
Successful login for user admin (remember_me: True, permanent_session: True)
Session restored from cookie for user admin (permanent: True, endpoint: index)

Troubleshooting

❌ “Authentication required” after login with HTTPS

Cause: ProxyFix not enabled or proxy not sending X-Forwarded-Proto: https

Solution:

  1. Turn on Trust proxy headers in App settings -> Configuration -> Reverse proxy
  2. Verify proxy is sending X-Forwarded-Proto: https header
  3. Restart MyAstroBoard (button in the same section, or restart the container)

❌ Cookies not persisting after browser restart

Cause: the HTTPS-only session cookie setting is on but the site is accessed via HTTP

Solution: Always access via HTTPS when that setting is on

❌ Cannot sign in, or sign out does nothing, over HTTP

Cause: the HTTPS-only session cookie setting (App settings -> Configuration -> Reverse proxy) is on, and the page is opened over plain HTTP (for example http://192.168.1.10:5000 on the local network). A browser neither stores nor deletes a Secure cookie on an HTTP page: a fresh sign-in is lost at once, and a session that existed before the setting was turned on can no longer be signed out.

Solution: turn the setting off from a page that still works: the browser that is still signed in, the HTTPS address, or http://localhost (browsers accept Secure cookies there), for example through an SSH tunnel: ssh -L 5000:localhost:5000 <host>. Since 1.8.2 the setting refuses to turn on from a plain-HTTP page.

❌ Sessions invalidated after container restart

Cause: the data/ directory is not persisted (no volume mapped), so the database holding the session key is recreated at each start.

Solution: map ./data:/app/data as in the provided docker-compose.yml.

❌ IP addresses in logs show proxy IP, not client IP

Cause: ProxyFix not enabled

Solution: Turn on Trust proxy headers (App settings -> Configuration -> Reverse proxy)


Security Best Practices

  1. ✅ Always use HTTPS in production with valid SSL certificates
  2. ✅ Enable Trust proxy headers only when behind a trusted reverse proxy
  3. ✅ Enable the HTTPS-only session cookie for HTTPS deployments
  4. ✅ Turn on two-factor authentication for accounts reachable from the internet (AUTHENTICATION.md)
  5. ✅ Keep Docker images updated (docker compose pull)
  6. ✅ Enable firewall rules to restrict direct access to port 5000
  7. ⚠️ Never expose port 5000 directly to the internet when using a reverse proxy

Quick Reference

DeploymentTrust proxy headersHTTPS-only session cookie
Local network, plain HTTPoffoff
Behind a reverse proxy with HTTPSonon

Additional Resources

Last updated 2026-11-13