2 · Environment domains + SSL
Objective — give each environment row in Zaj-PROJECT.md a reachable hostname and a valid certificate before the first deploy (Deployer flips a symlink, it doesn’t issue certs), so the deploy lands on a working HTTPS origin.
Steps at a glance:
- Plan the environment hostname map — Decide the hostnames up front so DNS, certs, and your app’s
APP_URLall agree. - Add the DNS records — Create one record per host at your DNS provider (or Cloudflare — see 6 · Cloudflare CDN).
- Provision the SSL/TLS certificate — Use your host’s automated Let’s Encrypt integration (cPanel AutoSSL,
Forge, Ploi, or
certbot). - Verify with five CLI checks — Don’t trust the browser — run these and read the output.
Background
Section titled “Background”Every non-production and production row in Zaj-PROJECT.md needs a reachable hostname and a valid certificate before the first deploy — Deployer flips a symlink, it doesn’t issue certs. This page gets DNS and TLS green so the deploy lands on a working HTTPS origin. The common one-staging setup is only an example; projects can use qa, uat, client-demo, sandbox, or several non-production rows.
1. Plan the environment hostname map
Section titled “1. Plan the environment hostname map”Decide the hostnames up front so DNS, certs, and your app’s APP_URL all agree.
| Environment key | Example host | Points to |
|---|---|---|
staging-primary | nonprod.example.com | Selected non-production server IP (or Cloudflare proxy) |
qa / uat / client-demo | qa.example.com / uat.example.com / demo.example.com | The server IP recorded for that environment row |
| Production | app.example.com / example.com | Production server IP |
| Apex redirect | example.com → www (or vice-versa) | Pick one canonical, 301 the other |
Keep apex vs www decisions consistent with APP_URL in 3 · Production .env. A mismatch breaks absolute URLs, signed routes, and cookie domains.
Record the final URL/domain, host/IP, and noindex/robots posture in Zaj-PROJECT.md before you create records. If there is no non-production URL yet, leave that row pending and do not enter Phase 5 until a real target is selected.
2. Add the DNS records
Section titled “2. Add the DNS records”Create one record per host at your DNS provider (or Cloudflare — see 6 · Cloudflare CDN).
| Type | Name | Value | Notes |
|---|---|---|---|
A | <non-production-host> | <server IPv4> | Use the host from the selected Zaj-PROJECT.md row |
AAAA | <non-production-host> | <server IPv6> | Only if the server has IPv6 |
CNAME | www | example.com | Alias one name to the canonical |
3. Provision the SSL/TLS certificate
Section titled “3. Provision the SSL/TLS certificate”Use your host’s automated Let’s Encrypt integration (cPanel AutoSSL, Forge, Ploi, or certbot). One cert per hostname, or a wildcard if your panel supports DNS-01.
- Issue for every hostname that serves traffic, including
www. - Enable auto-renewal — Let’s Encrypt certs expire every 90 days.
- Force HTTP → HTTPS at the web server (or via Cloudflare “Always Use HTTPS”).
4. Verify with five CLI checks
Section titled “4. Verify with five CLI checks”Don’t trust the browser — run these and read the output.
-
Run the five-check verification pass.
Terminal window HOST="nonprod.example.com" # replace with the selected URL/domain from Zaj-PROJECT.md# 1) DNS resolves to the expected IPdig +short "$HOST"# 2) HTTPS responds (expect HTTP/2 200 or 301/302 to the canonical host)curl -sI "https://$HOST" | head -1# 3) HTTP redirects to HTTPS (expect 301/308 with a https:// Location)curl -sI "http://$HOST" | grep -iE 'HTTP/|location'# 4) Behind Cloudflare? Confirm the proxy is in frontcurl -sI "https://$HOST" | grep -i 'cf-ray' && echo "proxied" || echo "direct"# 5) Certificate is valid and covers the hostecho | openssl s_client -connect "$HOST:443" -servername "$HOST" 2>/dev/null \| openssl x509 -noout -subject -issuer -dates# Expected: dig returns your IP, HEAD is 200/301, HTTP redirects to HTTPS, and the cert notAfter is in the future with subject matching the host- ✅
digreturns your IP, the HEAD request is200/301, HTTP redirects to HTTPS, and the cert’snotAfterdate is in the future andsubjectmatches the host.
- ✅
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
dig returns nothing / wrong IP | Record missing or not propagated | Re-check the record; wait for TTL |
curl cert error | Cert not issued / wrong host | Re-run AutoSSL; ensure host is in the cert SAN |
Hostinger SSL stays Installing | Let’s Encrypt validation cannot reach the current origin, often because Cloudflare is still proxied | Temporarily DNS-only the record for issuance, cancel/retry SSL, then re-proxy after the cert is active |
Cloudflare 525 on production | Cloudflare reached an origin with no valid TLS, or DNS still points to a stale/wrong origin | First run the stale-DNS check against the current origin IP; then grey-cloud if needed, issue the origin cert, re-proxy, and use Full (Strict) |
| Redirect loop | Cloudflare SSL mode Flexible + origin forcing HTTPS | Set Cloudflare SSL to Full (Strict) (see page 6) |
NET::ERR_CERT_COMMON_NAME_INVALID | Cert doesn’t list this host | Reissue covering all hostnames |
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🤖 Hostnames resolve — every environment hostname resolves via
digto the correct IP. - 🤖 HTTPS + redirect — HTTPS returns 200/301 and HTTP force-redirects to HTTPS.
- 🔀 Production Cloudflare safe — production is not orange-clouded until the stale-DNS check matches the current origin and origin TLS is valid; any live grey-cloud window had explicit approval and rollback notes.
- 👤 Cert valid + auto-renew — certificate is valid, covers the host, and auto-renewal is on.
- 🔀 Project state updated —
Zaj-PROJECT.mdrecords each environment URL/domain, DNS target, and noindex/robots posture. - 🔀 Canonical matches
APP_URL— apex vswwwdecision matches the plannedAPP_URL.