Zodiac Tech CRM Docs

Domain, subdomain & SSL

Point a (sub)domain at the install, issue a free certificate through cPanel AutoSSL or Let's Encrypt, force HTTPS and update APP_URL safely.

super-admin developer support Medium ⏱ 20 min Updated 2026-10-05

HTTPS is not optional for this product: Meta OAuth requires an HTTPS redirect URI, WhatsApp webhooks are signature-checked, push and OTP flows live in browsers and mobile apps, and cPanel AutoSSL issues certificates for free. This page gets a fresh (sub)domain from DNS to a green padlock.

Who this is for

  • Super-admin / hosting admin — wiring crm.yourdomain.com to the install.
  • Support — diagnosing redirect loops, mixed-content warnings and "Meta says redirect URI invalid".

Prerequisites: the app is already uploaded and the installer completed (cPanel deployment walkthrough).

Step 1 — Create the subdomain (DNS)

If your domain's nameservers already point at this host (the usual case for cPanel):

  1. cPanel → Domains → Create A New Domain → crm.yourdomain.com.
  2. The DNS A record is created automatically. Nothing else to do.

If the domain's DNS lives elsewhere (registrar, Cloudflare):

  1. Create the subdomain in cPanel first (document root as in the walkthrough).

  2. Then add a DNS record at the DNS provider:

    Type Name Value
    A crm your server's IP address

    (If using Cloudflare, start with the proxy off (grey cloud) while you issue the certificate, then decide about proxying — see Reverse proxies below.)

Expected result: crm.yourdomain.com resolves to the server (a 403/placeholder is fine at this stage).

Step 2 — Issue the certificate

Two free paths, both inside cPanel:

Option A — AutoSSL (most cPanel hosts):

  1. cPanel → SSL/TLS Status.
  2. Tick the domain → Run AutoSSL.
  3. Wait 1–5 minutes; refresh until the domain shows a padlock.

Option B — Let's Encrypt plugin (hosts with the plugin installed):

  1. cPanel → Let's Encrypt SSL (or "SSL/TLS → Manage SSL Hosts").
  2. Choose the domain → Issue.
  3. Wait for issuance to complete.

Expected result: https://crm.yourdomain.com/ loads without a browser warning.

Certificate fails to issue? The common causes are: DNS not yet resolving to this server, a Cloudflare proxy in front (the HTTP-01 challenge can be blocked), or the document root not existing yet. Fix DNS first, then re-run AutoSSL.

Step 3 — Force HTTPS

Pick one:

cPanel toggle (easiest): cPanel → Domains → find the subdomain → toggle Force HTTPS Redirect on. (The exact label varies by panel version.)

.htaccess rule (works everywhere): add below the existing RewriteEngine On line in the site's .htaccess (for a standard layout that file is public/.htaccess):

# Force HTTPS
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Behind Cloudflare? If Cloudflare terminates TLS (orange cloud), the X-Forwarded-Proto condition above prevents a redirect loop. Also set TRUSTED_PROXIES in .env to Cloudflare's IP ranges so Laravel sees real visitor IPs — otherwise the login lockout and audit log record Cloudflare's addresses. See Environment variables.

Step 4 — Update the application

Two values must reflect the final HTTPS URL, or generated links, OAuth redirects and webhook URLs will be wrong:

  1. .env:

    APP_URL=https://crm.yourdomain.com
    
    # On HTTPS deployments, also uncomment the session hardening block:
    SESSION_SECURE_COOKIE=true
    SESSION_HTTP_ONLY=true
    SESSION_SAME_SITE=lax
    

    (If the install lives in a subfolder and the document root is not public/, APP_URL must include the subfolder: https://crm.yourdomain.com/crm.)

  2. Clear compiled config so the change takes effect:

    cd ~/crm-app
    php artisan optimize:clear
    

    On no-SSH hosts, the same happens automatically the next time the app detects stale caches, but a quick config:clear through any terminal access is the reliable way.

Expected result: https://crm.yourdomain.com/api/v1/health returns {"status":"ok",...} and the browser shows a padlock with no mixed-content warnings.

Step 5 — Update your integrations

Any URL you already pasted into an external dashboard must be revisited if the domain changed:

Where What to update
Meta app dashboard OAuth redirect URI — taken from Super Admin → Meta Developer settings, which is built from APP_URL
WhatsApp Cloud API Callback URL — rebuilt from APP_URL on the WhatsApp connection page
Razorpay / Cashfree Webhook URLs /api/webhooks/razorpay|cashfree
Uptime monitor Point it at /api/v1/health

Full list: Meta / WhatsApp / payment callbacks.

Common mistakes

  • Leaving APP_URL=http://… while browsing over HTTPS — e-mails and OAuth redirect URIs come out http:// and break.
  • Forcing HTTPS while the certificate is still pending — visitors hit certificate errors.
  • Setting SESSION_SECURE_COOKIE=true before HTTPS works — nobody can log in (cookies never send over HTTP).
  • Changing the domain after WhatsApp was connected and forgetting the callback URL update.
  • Using a trailing slash in APP_URL — omit it.

Troubleshooting

Symptom Check
Redirect loop (ERR_TOO_MANY_REDIRECTS) A proxy (Cloudflare) with your own HTTPS rule — use the X-Forwarded-Proto condition, set TRUSTED_PROXIES
Padlock but "Not secure" assets Mixed content: some asset URL still http:// — fix APP_URL, clear caches
AutoSSL won't issue DNS resolution, existing redirect at the domain root, or a proxy blocking challenges
Login works on HTTP, fails on HTTPS SESSION_SECURE_COOKIE=true set before HTTPS was live, or APP_URL scheme mismatch
Mobile app can't reach the server The app requires https://…/api/v1/mobile/ with the correct scheme and trailing slash

Technical details

  • Runtime URL: config('app.url') ← APP_URL; used to build Meta OAuth redirects (route('oauth.callback', ['source' => 'meta'])) and the WhatsApp callback URL (rtrim(config('app.url'),'/') . '/api/messaging/whatsapp/webhook/{tenant}/{token}').
  • Session hardening keys: SESSION_SECURE_COOKIE, SESSION_HTTP_ONLY, SESSION_SAME_SITE (documented as commented defaults in .env.example).
  • Proxy trust: TRUSTED_PROXIES in .env; SA_IP_ALLOWLIST behavior depends on real client IPs.
  • Security headers on responses: public/.htaccess (X-Content-Type-Options, X-Frame-Options with the embeddable-widget exemption, Referrer-Policy).

Was this page helpful?