-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCaddyfile
More file actions
141 lines (120 loc) · 4.93 KB
/
Caddyfile
File metadata and controls
141 lines (120 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# ZeroAuth — Caddy reverse proxy config.
# Caddy provisions Let's Encrypt certs automatically for every vhost below.
#
# Usage on the production host:
# docker compose --profile prod up -d --build
# docker exec zeroauth-caddy caddy reload --config /etc/caddy/Caddyfile
#
# DNS prerequisites (operator action, before deploy):
# A zeroauth.dev → 104.207.143.14
# A www.zeroauth.dev → 104.207.143.14
# A api.zeroauth.dev → 104.207.143.14
# A console.zeroauth.dev → 104.207.143.14
# A docs.zeroauth.dev → 104.207.143.14
# ─── Shared snippets ────────────────────────────────────────────
(security_headers) {
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "interest-cohort=()"
}
}
(asset_caching) {
@hashed path_regexp hashed \.(js|css|woff2?|svg|png|jpg|webp)$
header @hashed Cache-Control "public, max-age=31536000, immutable"
}
(json_log) {
log {
output stdout
format json
}
}
# ─── Apex: marketing site + signup + apex 308s for legacy paths ─
zeroauth.dev, www.zeroauth.dev {
encode zstd gzip
# Legacy URLs from the pre-split era get a permanent redirect to
# the right subdomain. Use path_regexp with capture groups to keep
# the suffix (so /dashboard/login → console.zeroauth.dev/login).
@dash path_regexp dash ^/dashboard(/.*)?$
redir @dash https://console.zeroauth.dev{re.dash.1} permanent
@docs path_regexp doc ^/docs(/.*)?$
redir @docs https://docs.zeroauth.dev{re.doc.1} permanent
# 308 (not 301/permanent) so POST stays POST across the redirect.
# The landing page submits the whitepaper + pilot lead forms as
# POST to relative /api/leads/*; a 301 here was making the browser
# downgrade the second hop to GET, which 404'd on the API host
# and surfaced as a "Sorry, something went wrong" toast.
redir /v1/* https://api.zeroauth.dev{uri} 308
redir /api/* https://api.zeroauth.dev{uri} 308
reverse_proxy zeroauth-prod:3000 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
}
import asset_caching
import security_headers
import json_log
}
# ─── api.zeroauth.dev — REST surface ────────────────────────────
api.zeroauth.dev {
encode zstd gzip
reverse_proxy zeroauth-prod:3000 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
}
import security_headers
import json_log
}
# ─── console.zeroauth.dev — developer console ───────────────────
# The upstream Express app serves the dashboard SPA at /dashboard/*
# and the console JSON API at /api/console/*. The Vite-built bundle
# uses `/dashboard/` as its base, so its index.html references
# /dashboard/assets/X.js.
#
# Users see a bare-domain UX (console.zeroauth.dev/login, not
# /dashboard/login), so we prepend /dashboard when the path doesn't
# already carry it — BUT we also have to leave API paths alone, since
# the SPA fetches /api/console/* with relative URLs. Without this
# exception the rewrite turned every API call into /dashboard/api/...,
# Express's SPA catch-all returned HTML, fetch() got HTML where it
# expected JSON, React crashed, and the page rendered blank.
console.zeroauth.dev {
encode zstd gzip
@needs_prefix not path /dashboard /dashboard/* /api /api/* /v1 /v1/*
rewrite @needs_prefix /dashboard{uri}
reverse_proxy zeroauth-prod:3000 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
}
import asset_caching
import security_headers
import json_log
}
# ─── docs.zeroauth.dev — Docusaurus build ───────────────────────
# Docusaurus is now built with baseUrl '/' so the in-browser URL the
# client router matches against is the bare path the user sees. The
# Express upstream still mounts the static files under /docs/*, so
# Caddy rewrites the *server-side* request to add the prefix back —
# but the browser URL stays clean.
#
# Two extra rules guard the edges:
# 1. Old bookmarks that hit docs.zeroauth.dev/docs/X get a 301 to
# the new canonical URL '/X'. Without this the client router
# would 404 because no '/docs/*' route exists anymore.
# 2. /api/* and /v1/* skip the rewrite so the playground (and any
# future fetch) reaches the real API handlers, not the SPA
# catch-all served at /docs.
docs.zeroauth.dev {
encode zstd gzip
@legacy_docs_prefix path_regexp legacy ^/docs(/.*)?$
redir @legacy_docs_prefix https://docs.zeroauth.dev{re.legacy.1} permanent
@needs_prefix not path /api /api/* /v1 /v1/*
rewrite @needs_prefix /docs{uri}
reverse_proxy zeroauth-prod:3000 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
}
import asset_caching
import security_headers
import json_log
}