Build hosting-ssl
We now have:
hosting-create
hosting-list
hosting-info
hosting-health
hosting-repair
The next component is SSL automation.
The target command is:
sudo hosting-ssl example.com
It should convert:
HTTP
into:
HTTPS
without disturbing the existing website.
1. SSL Architecture
The complete flow becomes:
hosting-ssl
│
▼
Check domain
│
▼
Check DNS
│
▼
Check HTTP
│
▼
Request certificate
│
▼
Let's Encrypt
│
▼
Configure Nginx
│
▼
nginx -t
│
▼
Reload Nginx
│
▼
Test HTTPS
│
▼
Enable auto-renewal
2. Most Important Rule
SSL provisioning must not break HTTP.
Bad workflow:
request certificate
↓
modify Nginx
↓
configuration fails
↓
website DOWN
Better:
HTTP working
↓
certificate
↓
generate HTTPS configuration
↓
validate
↓
activate
3. Why DNS Comes First
For:
example.com
the DNS record should point to your server:
example.com
↓
SERVER IP
For a subdomain:
learn.cresignsys.com
↓
SERVER IP
The certificate authority must be able to verify that the domain resolves correctly.
4. Check DNS
From the server:
dig +short example.com
or:
nslookup example.com
Expected:
YOUR_SERVER_IP
If DNS points somewhere else:
hosting-ssl
↓
STOP
5. Don’t Guess DNS
Your SSL script should never assume:
DNS is correct
It should verify the domain’s DNS resolution.
For a production system, DNS verification should be explicit.
6. Check HTTP First
Before SSL:
curl -I http://example.com
You want a valid HTTP response.
For example:
HTTP/1.1 200 OK
or potentially:
HTTP/1.1 301 Moved Permanently
depending on your existing configuration.
7. Why HTTP Must Work
With common HTTP-based certificate validation:
Certificate Authority
↓
http://example.com/.well-known/...
↓
Nginx
↓
server
If Nginx isn’t correctly serving the domain, certificate issuance can fail.
8. Install Certbot
On Ubuntu, check whether Certbot is already installed:
certbot --version
If not installed, install it using the package-management method appropriate for your Ubuntu deployment.
For a production CHP server, keep the certificate-management tool standardized rather than mixing several ACME clients.
9. Check Nginx Plugin
If using Certbot with Nginx:
certbot plugins
Look for:
nginx
The important idea is:
Certbot
↓
ACME
↓
Let's Encrypt
10. First Certificate Test
Before allowing automated production changes, use a staging/test environment when developing the automation.
This avoids hitting certificate authority rate limits while debugging.
The general flow is:
staging
↓
test successful
↓
production certificate
11. Why Staging Matters
Imagine your script has a bug and requests:
20 certificates
during development.
You could encounter certificate authority rate limits.
Therefore:
DEVELOPMENT
→ staging
PRODUCTION
→ production CA
12. Create hosting-ssl
Create:
sudo nano /usr/local/bin/hosting-ssl
Start:
#!/usr/bin/env bash
set -Eeuo pipefail
13. Require Root
if [[ "$EUID" -ne 0 ]]; then
echo "ERROR: Run with sudo."
exit 1
fi
14. Require Domain
if [[ $# -ne 1 ]]; then
echo "Usage: hosting-ssl DOMAIN"
exit 1
fi
DOMAIN="$1"
15. Locate Site
STATE_DIR="/var/lib/cresignsys/sites"
SITE_DIR="${STATE_DIR}/${DOMAIN}"
SITE_CONF="${SITE_DIR}/site.conf"
Then:
if [[ ! -f "$SITE_CONF" ]]; then
echo "ERROR: CresignSys site not found."
exit 1
fi
16. Load Metadata
source "$SITE_CONF"
Now the script knows:
DOMAIN
SITE_ID
SITE_USER
WEB_ROOT
PHP_VERSION
PHP_SOCKET
STATUS
17. Verify Required Commands
Check:
command -v certbot
command -v dig
command -v nginx
command -v curl
If anything important is missing:
ERROR: Required dependency missing.
Stop before modifying anything.
18. Check Nginx
if ! systemctl is-active --quiet nginx; then
echo "ERROR: Nginx is not running."
exit 1
fi
19. Check Existing Configuration
NGINX_CONF="/etc/nginx/sites-available/${DOMAIN}.conf"
Verify:
if [[ ! -f "$NGINX_CONF" ]]; then
echo "ERROR: Nginx site configuration not found."
exit 1
fi
20. Validate Nginx Before SSL
Always:
nginx -t
If it fails:
SSL provisioning
↓
STOP
Fix the existing website first:
hosting-repair example.com --nginx
21. Check DNS
A simple check:
DNS_IP="$(dig +short "$DOMAIN" A | tail -n 1)"
Then display:
Domain:
example.com
Resolved IP:
xxx.xxx.xxx.xxx
22. Compare With Server IP
You can determine the server’s public addresses using the server/network configuration.
But be careful with:
IPv4
IPv6
NAT
reverse proxies
CDNs
load balancers
A simplistic:
DNS IP == local interface IP
check is not universally correct.
Therefore DNS validation should be configurable.
23. HTTP Validation
Run:
curl -fsSI --max-time 10 "http://${DOMAIN}"
If this fails:
ERROR: HTTP validation failed.
Do not request SSL yet.
24. Why This Prevents Problems
The dependency chain is:
DNS
↓
HTTP
↓
Certificate
↓
HTTPS
You shouldn’t attempt step 3 when step 2 is broken.
25. Certificate Request
Once DNS and HTTP are confirmed, request the certificate.
Conceptually:
certbot certonly \
--nginx \
-d "$DOMAIN"
For automated server provisioning, use non-interactive settings and a defined administrative email.
Do not hard-code a personal email address into the platform.
26. Why certonly?
We want CHP to remain the owner of the Nginx configuration.
Therefore:
Certbot
↓
certificate management
CHP
↓
Nginx configuration
This gives you greater control.
An alternative architecture is to let Certbot modify Nginx automatically, but mixing two configuration managers can make your platform harder to reason about.
27. Certificate Location
Certificates are commonly maintained under:
/etc/letsencrypt/
For a domain, you’ll typically find a lineage such as:
/etc/letsencrypt/live/example.com/
with certificate-related files.
Don’t copy private keys into:
/storage/websites/example.com/public/
Never.
28. Private Key Security
The TLS private key is sensitive.
It belongs in:
/etc/letsencrypt/
or another protected system location managed by your certificate system.
It must never be:
publicly accessible
downloadable
stored in WordPress
stored under document root
29. Generate HTTPS Nginx Configuration
Your Nginx configuration now needs:
HTTP
HTTPS
A common architecture is:
HTTP :80
↓
redirect
↓
HTTPS :443
and:
HTTPS :443
↓
PHP-FPM
30. HTTP Server Block
Conceptually:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
But there is an important issue:
ACME HTTP validation may need access to the challenge path.
Therefore your certificate issuance and final redirect configuration need to be coordinated.
31. HTTPS Server Block
Conceptually:
server {
listen 443 ssl http2;
server_name example.com;
root /storage/websites/example.com/public;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/csp1027.sock;
}
}
Exact TLS directives should be maintained in a tested central template rather than copied independently for every site.
32. Don’t Duplicate Configuration
Your architecture should eventually use:
/etc/cresignsys/templates/
├── nginx-http.conf
└── nginx-https.conf
or one carefully designed template with SSL variables.
33. Certificate Variables
The template needs:
DOMAIN
WEB_ROOT
PHP_SOCKET
SSL_CERTIFICATE
SSL_CERTIFICATE_KEY
For example:
SSL_CERTIFICATE:
/etc/letsencrypt/live/example.com/fullchain.pem
SSL_CERTIFICATE_KEY:
/etc/letsencrypt/live/example.com/privkey.pem
34. Validate Before Activation
Generate the configuration.
Then:
nginx -t
Only if successful:
systemctl reload nginx
This rule remains unchanged.
35. Test HTTPS
After reload:
curl -I https://example.com
Expected:
HTTP/2 200
or another legitimate successful/redirect response depending on your configuration.
36. Verify Certificate
Use:
openssl s_client \
-connect "${DOMAIN}:443" \
-servername "$DOMAIN" \
</dev/null
This lets you inspect the TLS connection.
For automated health checking, a simpler certificate-expiry inspection can also be used.
37. Certificate Expiry
A certificate has an expiration date.
Your platform should know:
Certificate:
ACTIVE
Expires:
2026-11-11
Days remaining:
90
The exact lifetime depends on the certificate authority’s current policies.
38. Why Renewal Is Essential
SSL automation isn’t complete when the certificate is issued.
It is complete only when:
certificate
↓
renewal
↓
renewal validation
↓
Nginx reload
works automatically.
39. Check Renewal Configuration
Certbot typically provides a renewal mechanism through the system’s scheduled service/timer.
Check:
systemctl list-timers | grep -i certbot
Depending on how Certbot was installed, the exact scheduling mechanism can differ.
40. Test Renewal
Do not wait until the certificate is nearly expired.
Use the certificate tool’s dry-run renewal mechanism.
For Certbot:
certbot renew --dry-run
This tests the renewal process without replacing the production certificate.
41. Renewal Architecture
The desired system:
Certificate
│
▼
Renewal Scheduler
│
▼
Renew Certificate
│
▼
Validate Nginx
│
▼
Reload Nginx
│
▼
HTTPS Health Check
42. Important: Renewal Must Not Break Nginx
A renewal system should verify:
new certificate
↓
Nginx configuration
↓
nginx -t
↓
reload
before declaring success.
43. Update site.conf
After successful SSL:
SSL_STATUS=ACTIVE
Add:
SSL_CERTIFICATE=/etc/letsencrypt/live/example.com/fullchain.pem
SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/example.com/privkey.pem
Again, storing the path is fine; don’t store the private key itself.
44. Update Site State
Before:
STATUS=ACTIVE
SSL_STATUS=NOT_CONFIGURED
After:
STATUS=ACTIVE
SSL_STATUS=ACTIVE
45. Health Check Changes
Previously:
HTTP ✓
Now:
HTTP → redirect
HTTPS ✓
Certificate ✓
Your hosting-health command should understand the new expected state.
46. Final Health Model
For an SSL-enabled website:
DNS ✓
HTTP ✓
HTTPS ✓
Certificate ✓
Certificate age ✓
Nginx ✓
PHP-FPM ✓
PHP socket ✓
WordPress ✓
Database ✓
Filesystem ✓
47. HTTP Redirect Test
After SSL:
curl -I http://example.com
Expected:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/...
Then:
curl -I https://example.com
should succeed.
48. Avoid Redirect Loops
A common mistake is creating:
HTTP → HTTPS
HTTPS → HTTP
or proxy configurations that cause:
HTTP
↓
HTTPS
↓
HTTP
↓
HTTPS
Your health check should detect this.
49. HTTPS Redirect Architecture
The desired flow is:
http://example.com
│
▼
301
│
▼
https://example.com
│
▼
WordPress
Never:
HTTPS → HTTP
unless deliberately required for a specific architecture.
50. WordPress URL Update
After enabling HTTPS, WordPress must know that its canonical URL is HTTPS.
Check:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get siteurl
and:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get home
They should eventually reflect:
https://example.com
51. Update WordPress URLs Carefully
A controlled command can update:
wp option update siteurl "https://example.com"
wp option update home "https://example.com"
But this is only part of the HTTPS migration.
Existing database content may contain:
http://example.com
inside serialized data or content.
52. Don’t Use Blind SQL Replacement
Avoid:
UPDATE wp_posts
SET post_content = REPLACE(...)
as a generic WordPress migration technique.
WordPress data can contain serialized structures.
Use WP-CLI’s WordPress-aware search/replace functionality for appropriate migrations.
53. Mixed Content
After HTTPS:
Page = HTTPS
Image = HTTP
CSS = HTTP
JS = HTTP
can produce:
mixed content
The site may technically load but still have insecure resources.
Therefore SSL health should eventually include an application-level check.
54. SSL Is More Than a Certificate
A working HTTPS deployment means:
DNS
+
certificate
+
Nginx
+
HTTPS
+
WordPress URL
+
resources
+
renewal
not simply:
certificate exists
55. hosting-ssl Output
A good result:
CresignSys SSL
==============
Domain: example.com
[OK] DNS resolution
[OK] HTTP service
[OK] Certificate issued
[OK] Certificate files
[OK] Nginx configuration
[OK] Nginx reload
[OK] HTTPS response
[OK] Certificate verification
[OK] WordPress HTTPS URL
SSL Status: ACTIVE
56. Failure Example
Suppose DNS is wrong:
CresignSys SSL
==============
Domain: example.com
[FAIL] DNS resolution
SSL Status: NOT CONFIGURED
HTTP website has not been modified.
That final line is extremely important.
57. Another Failure
Suppose certificate issuance succeeds but Nginx validation fails:
[OK] Certificate issued
[OK] Certificate files
[FAIL] Nginx configuration
SSL Status: ERROR
Existing HTTP configuration preserved.
The site should remain available over its previous working configuration.
58. This Requires Atomic Configuration Updates
Don’t overwrite the working Nginx configuration directly.
Instead:
current.conf
then generate:
current.conf.tmp
Validate:
nginx -t
Then replace:
current.conf.tmp
↓
current.conf
and reload.
59. Why This Matters
You want:
WORKING CONFIG
│
▼
NEW CONFIG
│
▼
VALIDATE
│
┌──┴──┐
▼ ▼
PASS FAIL
│ │
▼ ▼
activate preserve old
This is a general safe deployment pattern.
60. SSL State Machine
The SSL subsystem can have:
NOT_CONFIGURED
↓
REQUESTING
↓
ISSUED
↓
CONFIGURING
↓
ACTIVE
Failure:
REQUESTING
↓
ERROR
or:
CONFIGURING
↓
ERROR
61. Renewal States
Later:
ACTIVE
↓
RENEWAL_DUE
↓
RENEWING
↓
RENEWED
↓
ACTIVE
If renewal fails:
RENEWAL_ERROR
This allows the control panel to warn administrators before a certificate expires.
62. Certificate Monitoring
Your dashboard could eventually show:
DOMAIN SSL
------------------------------------------------
learn.cresignsys.com ACTIVE — 72 days
shop.cresignsys.com ACTIVE — 81 days
example.com ERROR
oldsite.com EXPIRING — 8 days
This is much more useful than simply:
SSL: ON
63. Add SSL to hosting-info
Now:
sudo hosting-info example.com
can show:
SSL Status : ACTIVE
Certificate : /etc/letsencrypt/live/example.com/fullchain.pem
Certificate Exp. : 2026-11-11
64. Add SSL to hosting-health
The health check becomes:
[OK] DNS
[OK] HTTP
[OK] HTTPS
[OK] Certificate
[OK] PHP-FPM
[OK] PHP Socket
[OK] Nginx
[OK] WordPress
[OK] Database
65. Current CHP Architecture
We now have:
CHP
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Provisioning Observability Repair
│ │ │
hosting-create hosting-list hosting-repair
hosting-info
hosting-health
│
▼
SSL
│
▼
hosting-ssl
66. Full Website Lifecycle
The lifecycle is becoming:
CREATE
↓
HTTP
↓
SSL
↓
HTTPS
↓
HEALTH
↓
REPAIR
↓
BACKUP
↓
SUSPEND
↓
RESTORE
↓
DELETE
67. Lesson 069 — Core Principle
SSL automation should be treated as a deployment process, not a single certificate command.
The safe workflow is:
DNS
↓
HTTP
↓
Certificate
↓
Temporary configuration
↓
Nginx validation
↓
Activation
↓
HTTPS test
↓
WordPress HTTPS
↓
Renewal test
↓
HEALTHY
And the most important safety rule is:
Never sacrifice a working website merely because SSL provisioning failed.
Next Lesson — 070
Build hosting-backup
The next major component is data protection.
We will design:
sudo hosting-backup example.com
to back up:
WordPress files
+
MySQL database
+
site configuration
+
SSL metadata
and produce a structured backup such as:
/backup/cresignsys/example.com/
├── 2026-08-13/
│ ├── files.tar.zst
│ ├── database.sql.gz
│ ├── site.conf
│ └── manifest.json
Then we can safely make hosting-repair, hosting-restore, and eventually automatic disaster recovery work together.
Leave a Reply