WordPress 建站指南 HTTPS and SSL Certificate Configuration Guide
Important note upfront: This article solves 4 specific problems — Nginx HTTPS config, Let's Encrypt certificate issuance, Certbot auto-renewal setup, and mixed content warning fixes. Based on real production environment experience, not generic tutorials.
---
Why HTTPS Matters More for WordPress Now
Since 2024, Google marks HTTP pages as "not secure." More importantly, modern browsers block certain features (geolocation, notifications) on HTTP pages, and HTTP/2 and HTTP/3 both require TLS. Without HTTPS, your WordPress site is already losing on speed and functionality.
The HTTP-01 challenge requires port 80 — that's a hard blocker for many users. My approach: DNS-01 challenge, which supports wildcard certs and requires only a domain registrar API token. I use Cloudflare with an API token, so the entire issuance process bypasses port 80 entirely. This is ideal for users with strict network restrictions.
Between the two main tools, Certbot is still the official recommendation, while acme.sh is the lighter alternative. Certbot updated to v5.5.0 in April 2026 and now supports the latest EAB (External Account Binding) requirements. acme.sh last updated to version 3.1.2 in November 2025 and remains stable. I've used both — if you're on Ubuntu 24.04 + Nginx, Certbot is the least friction.
---
Trap 1: WordPress Admin Goes White Screen After HTTPS Config
Symptom: After configuring HTTPS, wp-admin shows a blank screen or redirect loop.
Root cause: WordPress doesn't know the site is now HTTPS, keeps generating HTTP URLs.
Fix: Add these two lines to wp-config.php:
define('WP_HOME', 'https://' . $_SERVER['HTTP_HOST']);
define('WP_SITEURL', 'https://' . $_SERVER['HTTP_HOST']);
Or use wp-cli for quick fix:
wp option update home 'https://yourdomain.com'
wp option update siteurl 'https://yourdomain.com'
My mistake: configured Nginx HTTPS first, but ignored WordPress's own URL settings. The correct order is: fix WordPress config first, then Nginx HTTPS.
---
Trap 2: Certbot Fails to Connect Port 80
**Symptom**: Failed to connect to port 80 error during certificate request.
Root cause: Apache or another Nginx process is already using port 80.
Debug command:
# Check what's listening on port 80
sudo lsof -i :80
# or
sudo netstat -tlnp | grep :80
Fix: Stop the conflicting process then retry, OR bypass port 80 with DNS-01:
# Install certbot Cloudflare DNS plugin
sudo apt install python3-certbot-dns-cloudflare
# Use Cloudflare DNS verification (no port 80 needed)
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /path/to/cloudflare.ini \
-d yourdomain.com -d "*.yourdomain.com"
I use Cloudflare, so DNS verification is my go-to. Key point: after DNS-01 verification, the wildcard certificate gets automatically configured by Cloudflare — no manual Nginx cert path update needed.
---
Trap 3: Certificate Issued but Browser Shows "Incomplete Certificate Chain"
Symptom: Online tool (like SSLLabs) reports "Certificate chain incomplete."
Root cause: Let's Encrypt uses RSpec protocol to issue certs, but server only configured with leaf certificate — missing intermediate certificate.
Fix: In Nginx config, point to the full chain:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
Critical: fullchain.pem contains the complete chain (leaf + intermediate), while cert.pem is leaf only. The common mistake is using cert.pem, which produces an incomplete chain.
SSLLabs score went from C to A in about 10 minutes after fix. Since the certificate was already issued correctly, fixing the chain doesn't require re-issuance — just reload Nginx.
---
Trap 4: Mixed Content Warnings (Most Common)
**Symptom**: Console error Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'.
Root cause: Page references HTTP resources (images/CSS/JS) even though the site itself is HTTPS-configured.
Debug method: Open Chrome DevTools → Console, search "Mixed Content", find the specific URLs.
Complete fix with wp-cli scan and replace:
# Backup database first
wp db export backup.sql
# Scan all mixed content URLs (dry run first)
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --dry-run
# Execute after confirming
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com'
Quick mitigation via Nginx config: Add HSTS header and let browser auto-upgrade mixed content:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "upgrade-insecure-requests" always;
The upgrade-insecure-requests directive tells the browser to automatically upgrade all HTTP resource requests in the page to HTTPS. This is the fastest way to make old posts (with many HTTP links in the database) display correctly — no need to fix each database entry individually.
---
Trap 5: Certbot Auto-Renewal Fails
Symptom: Certificate expires, site turns red (insecure), systemd logs show renewal failure.
**Root cause**: Nginx reload needs graceful restart, not restart — but some people configure require tty in reload step, which blocks cron execution.
**Correct renewal config**: Edit /etc/letsencrypt/renewal/yourdomain.com.conf, ensure pre_hook and post_hook are correct:
pre_hook = systemctl stop nginx
post_hook = systemctl start nginx
**Do NOT use** reload — use start/stop instead, because Nginx reload doesn't always activate new certificates in some configurations.
Test renewal (don't wait until expiry):
# Simulate renewal process (won't actually issue)
sudo certbot renew --dry-run
# If dry-run passes, real renewal runs automatically in background
# If it fails, systemd timer notifies you
My lesson: dry-run passed, but actual expiry failed. Dry-run uses standalone mode (stopped Nginx), while real renewal ran with Nginx active, causing port conflict. So make sure pre_hook actually stops Nginx.
---
Complete Nginx HTTPS Config Template
Here's my production-verified config for Ubuntu 24.04 + Nginx 1.30:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
# TLS version control (disable TLS 1.0/1.1)
ssl_protocols TLSv1.2 TLSv1.3;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# CSP header: auto-upgrade mixed content
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# SSL session optimization
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
root /var/www/html;
index index.php index.html;
client_max_body_size 100M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.well-known/acme-challenge/ {
allow all;
}
}
Validate config before reloading: sudo nginx -t.
---
Tools to Verify Your Setup
After issuance, don't rely on browser alone — use professional tools:
1. SSLLabs (https://www.ssllabs.com/ssltest/): A grade is perfect, C grade means chain problem
2. Mozilla SSL Config Generator (https://ssl-config.mozilla.org/): Generate latest secure config
3. CRT.sh (https://crt.sh/): Query certificate issuance status
I used CRT.sh to verify Let's Encrypt intermediate certificate fingerprint, compared against Nginx's chain.pem, and confirmed before declaring the full setup complete.
---
Complete HTTPS Setup Workflow
1. Fix WordPress admin URL first (wp-cli or admin settings) → prevents white screen
2. Issue certificate (Certbot standalone or DNS-01) → port 80 issues resolved here
3. Configure Nginx HTTPS (fullchain.pem, TLS 1.2+1.3) → fixes incomplete chain
4. Add CSP header (upgrade-insecure-requests) → quick fix for mixed content
5. Replace database URLs permanently (wp-cli search-replace) → root cause fix for mixed content
6. Test renewal (certbot renew --dry-run) → ensure auto-renewal works
The order matters. WordPress config before Nginx, certificate before Nginx reload, DNS verification for bypassing port 80 restrictions.
---
What you might also need: If you deploy WordPress via Docker, SSL configuration is more complex (needs Docker 容器化部署-compose certificate path mounting). I've been through that too — next article will cover Docker + HTTPS setup in detail.
👉 Join now: https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link
🔗 Related Tech Articles
Deep dive into related technical topics: