Skip to main content

max / makenotwork

Name the public bucket in the CDN Caddy block The block had a BUCKET_NAME placeholder that prod never substituted, so the deployed cdn.makenot.work reverse-proxied a nonexistent bucket. Name the bucket and say why it must be S3_PUBLIC_BUCKET: that block has no signature check, so pointing it at the main bucket would serve every paid download unsigned.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-30 00:51 UTC
Signed with PGP, not checked
Commit: 1243c05be273054a86174d6e606e502fdeb2ef2a
Parent: bb329dd
1 file changed, +12 insertions, -5 deletions
@@ -135,9 +135,11 @@
135 135 }
136 136 }
137 137
138 - # CDN for free content downloads: reverse-proxies to Hetzner Object Storage.
138 + # CDN for promoted public images: reverse-proxies to Hetzner Object Storage.
139 139 # Cloudflare caches responses at the edge (free egress). Origin only hit on cache miss.
140 - # Requires: S3 bucket policy allowing public s3:GetObject, Cloudflare DNS A record (proxy ON).
140 + # Requires: public-read s3:GetObject policy on the public bucket, Cloudflare DNS A
141 + # record for cdn.makenot.work (proxy ON), and CDN_BASE_URL=https://cdn.makenot.work
142 + # so the app emits edge URLs rather than direct-origin ones.
141 143 cdn.makenot.work {
142 144 import cloudflare_tls
143 145
@@ -145,9 +147,14 @@
145 147 @not_get not method GET HEAD
146 148 respond @not_get 405
147 149
148 - # Prepend bucket name to URI path and proxy to Hetzner Object Storage.
149 - # Replace BUCKET_NAME with the actual S3 bucket name.
150 - rewrite * /BUCKET_NAME{uri}
150 + # Prepend the bucket name to the URI path and proxy to Hetzner Object Storage.
151 + # This bucket MUST be the one named by the app's S3_PUBLIC_BUCKET, and MUST NOT
152 + # be S3_BUCKET: there is no signature check here, so pointing it at the main
153 + # bucket would serve every paid download unsigned. Only immutably-public image
154 + # content (covers, gallery, item/project images) is promoted into the public
155 + # bucket; audio, video, and downloads stay private and are always presigned by
156 + # the app (see src/routes/storage/downloads.rs).
157 + rewrite * /makenotwork-public{uri}
151 158 reverse_proxy https://fsn1.your-objectstorage.com {
152 159 header_up Host fsn1.your-objectstorage.com
153 160 }