# ipx-web (#203): the frontend as static files, and everything else passed to ipx, on one origin, # so the session cookie, live updates and Cloudflare Access work as they did when ipx served it. # IPX_UPSTREAM where ipx is, host:port (ipx:8099) # WEB_BIND where to listen (:8080) # TUNNEL_PEER the one IPv4 address whose Cf-Access-* headers are passed on: where cloudflared's # requests come from. Nobody else's are, so nobody else can claim to be signed in # by Cloudflare. Unset passes none. { admin off auto_https off } http://{$WEB_BIND::8080} { root * {$WEB_ROOT:/srv} # Before anything else, so no page or API call sees them: only ipx sets these. Both forms of # the peer: Caddy listens dual-stack, so an IPv4 peer arrives as ::ffff:a.b.c.d, which the # plain address does not match, and the tunnel's headers were stripped too (#209). @forged not remote_ip {$TUNNEL_PEER:255.255.255.255} ::ffff:{$TUNNEL_PEER:255.255.255.255} request_header @forged -Cf-Access-Authenticated-User-Email request_header @forged -Cf-Access-Jwt-Assertion @api path /api/* /media/* handle @api { # Live updates are a stream: flushed as they come, never buffered. reverse_proxy {$IPX_UPSTREAM:ipx:8099} { flush_interval -1 } } # Signing in with the shared token in the address (?token=): ipx sets the session cookie and # sends the browser on to the page without it. @tokened { path / /admin query token=* } handle @tokened { rewrite * /api/signin?{query} reverse_proxy {$IPX_UPSTREAM:ipx:8099} } # The app and the admin page go to whoever ipx says may have them: a browser not signed in is # sent to /login, anyone but an admin asking for /admin to the app. @page path / /index.html /admin /admin.html /admin.js handle @page { forward_auth {$IPX_UPSTREAM:ipx:8099} { uri /api/page-check } rewrite / /index.html rewrite /admin /admin.html header Cache-Control no-cache file_server } # The sign-in form, except for someone Cloudflare Access vouches for, who is sent to the app. @login path /login /login.html handle @login { forward_auth {$IPX_UPSTREAM:ipx:8099} { uri /api/login-check } rewrite * /login.html header Cache-Control no-cache file_server } # Scripts and the stylesheet are named by a hash of their contents (web/build.mjs), so a browser # keeps them a year; the icons a day, under names that do not change. @hashed path /app.js /admin.js /login.js /app.css /hls.js /boot.js header @hashed Cache-Control "public, max-age=31536000, immutable" # The same PNG as /favicon.png, for a browser that asks for /favicon.ico on its own. header /favicon.ico Content-Type image/png @icons path *.png *.svg *.ico *.woff2 header @icons Cache-Control "public, max-age=86400" handle { file_server } }