Skip to content

Commit f2c3757

Browse files
committed
fix: drop render customDomains, use Cloudflare proxy for backend
Render's free plan rejects customDomains in render.yaml at apply time (reported error: 'Custom domains require a paid plan'). Switch to Cloudflare proxy + Universal SSL to keep a custom domain for free. - render.yaml: remove customDomains block, update BACKEND_URL comment - variables.tf: backend_subdomain default 'api' -> 'senzen-api' (one level deep under pantorn.site, covered by Cloudflare Universal SSL; two levels would need a paid Advanced cert) - cloudflare.tf: backend record proxied = true (was false) with comment explaining the Cloudflare 'Full' SSL mode workaround; frontend stays DNS-only (Vercel issues its own cert via ACME HTTP-01 which breaks behind orange cloud) New public URLs: Frontend: https://senzen.pantorn.site (Vercel) Backend: https://senzen-api.pantorn.site (Cloudflare → Render)
1 parent 24c3103 commit f2c3757

3 files changed

Lines changed: 33 additions & 17 deletions

File tree

infra/terraform/cloudflare.tf

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,47 @@
33
# =============================================================
44
# This file manages the DNS records for the Senzen project.
55
# Records:
6-
# senzen CNAME → cname.vercel-dns.com (frontend)
7-
# api.senzen CNAME → <render-service>.onrender.com (backend)
6+
# senzen CNAME → cname.vercel-dns.com (frontend, DNS-only)
7+
# senzen-api CNAME → <render-service>.onrender.com (backend, Cloudflare-proxied)
88
#
9-
# Both are DNS-only (proxied = false) so Vercel/Render can
10-
# issue and serve their own TLS certificates. The orange-cloud
11-
# proxy would break ACME HTTP-01 challenges for cert issuance.
9+
# The backend is proxied (orange cloud) because Render's free
10+
# plan does NOT support custom domains — so the service runs
11+
# at senzen-api.onrender.com, and Cloudflare terminates TLS
12+
# for senzen-api.pantorn.site using Universal SSL, then
13+
# forwards to Render in "Full" SSL mode (no cert verification).
14+
#
15+
# Both records are ONE level deep under pantorn.site so they're
16+
# covered by Cloudflare's Universal SSL (*.pantorn.site).
1217
# =============================================================
1318

1419
# ─────────────────────────────────────────────────────────────
15-
# Frontend: senzen.pantorn.site → Vercel
20+
# Frontend: senzen.pantorn.site → Vercel (DNS-only)
21+
# Vercel issues its own Let's Encrypt cert via ACME HTTP-01,
22+
# which fails if Cloudflare proxy is on. So we leave it
23+
# proxied = false and let Vercel handle TLS.
1624
# ─────────────────────────────────────────────────────────────
1725
resource "cloudflare_record" "senzen_frontend" {
1826
zone_id = var.cloudflare_zone_id
1927
name = split(".", var.frontend_domain)[0] # "senzen" from "senzen.pantorn.site"
2028
type = "CNAME"
2129
value = var.vercel_cname_target
2230
proxied = false
23-
comment = "Senzen frontend → Vercel"
31+
comment = "Senzen frontend → Vercel (DNS-only, Vercel issues cert)"
2432
}
2533

2634
# ─────────────────────────────────────────────────────────────
27-
# Backend: api.senzen.pantorn.site → Render
35+
# Backend: senzen-api.pantorn.site → Render (proxied)
36+
# Render free plan doesn't issue certs for custom domains.
37+
# Cloudflare proxies, terminates TLS with Universal SSL, and
38+
# forwards to Render's onrender.com URL. Set Cloudflare zone
39+
# SSL mode to "Full" (NOT "Full (Strict)") so the origin cert
40+
# isn't required to be valid for senzen-api.pantorn.site.
2841
# ─────────────────────────────────────────────────────────────
2942
resource "cloudflare_record" "senzen_backend" {
3043
zone_id = var.cloudflare_zone_id
31-
name = "${var.backend_subdomain}.${split(".", var.frontend_domain)[0]}" # "api.senzen"
44+
name = var.backend_subdomain # "senzen-api"
3245
type = "CNAME"
3346
value = "${var.render_service_name}.onrender.com"
34-
proxied = false
35-
comment = "Senzen backend → Render (${var.render_service_name}.onrender.com)"
47+
proxied = true
48+
comment = "Senzen backend → Render via Cloudflare proxy (Render free doesn't support custom domains)"
3649
}

infra/terraform/variables.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ variable "frontend_domain" {
6363
}
6464

6565
variable "backend_subdomain" {
66-
description = "Subdomain label for the backend (relative to frontend_domain). Final URL: <backend_subdomain>.<frontend_domain>"
66+
description = "Subdomain label for the backend (one level deep under cloudflare_zone_name). Must be ONE level deep to be covered by Cloudflare Universal SSL. Final URL: <backend_subdomain>.<cloudflare_zone_name>"
6767
type = string
68-
default = "api"
68+
default = "senzen-api"
6969
}
7070

7171
# ─────────────────────────────────────────────────────────────

render.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
# Free plan = 512 MB RAM, 0.1 vCPU, sleeps after 15 min idle.
99
# Auto-deploys on every push to main.
1010
#
11+
# NOTE on custom domains: render.yaml does NOT support custom
12+
# domains on the free plan. The service runs at
13+
# https://senzen-api.onrender.com
14+
# and Cloudflare proxies the public URL (senzen-api.pantorn.site)
15+
# to it via "Full" SSL mode. See infra/terraform/cloudflare.tf.
16+
#
1117
# Sensitive env vars are marked `sync: false` — you set the
1218
# values once in the Render dashboard, and they stay out of git.
1319
# =============================================================
@@ -40,7 +46,7 @@ services:
4046
- key: FRONTEND_URL
4147
value: https://senzen.pantorn.site
4248
- key: BACKEND_URL
43-
value: https://api.senzen.pantorn.site
49+
value: https://senzen-api.pantorn.site
4450
- key: GO_ENV
4551
value: production
4652

@@ -61,6 +67,3 @@ services:
6167
sync: false
6268
- key: GITHUB_CLIENT_SECRET
6369
sync: false
64-
65-
customDomains:
66-
- name: api.senzen.pantorn.site

0 commit comments

Comments
 (0)