Moving your WordPress site from HTTP to HTTPS is no longer optional. Browsers flag HTTP sites as "Not Secure," Google uses HTTPS as a ranking signal, and your visitors' data is transmitted in plain text without encryption. After installing an SSL certificate, you need to properly redirect all HTTP traffic to HTTPS and fix any remaining mixed content issues. This guide covers every step of the process, from obtaining a certificate to debugging redirect loops behind load balancers.
Why HTTPS Matters Beyond Basic Security
The obvious reason for HTTPS is encryption. Without it, everything between your visitor's browser and your server (form submissions, login credentials, cookies, personal data) can be intercepted by anyone on the same network. But HTTPS matters for several other reasons that directly affect your site's success:
- SEO ranking factor: Google confirmed in August 2014 that HTTPS is a ranking signal. While it is a lightweight signal, all else being equal, the HTTPS version of a page will rank higher than the HTTP version. In competitive niches, every signal counts.
- Browser warnings: Chrome, Firefox, Safari, and Edge all display "Not Secure" warnings for HTTP pages, especially those with form inputs. This erodes visitor trust and increases bounce rates. Chrome has been progressively making these warnings more prominent since Chrome 68 (July 2018).
- HTTP/2 and HTTP/3 support: Modern protocols like HTTP/2 and HTTP/3, which dramatically improve page load speed through multiplexing and header compression, require HTTPS in practice. All major browsers only support HTTP/2 over TLS.
- Referrer data: When a visitor clicks a link from an HTTPS page to an HTTP page, the browser strips the referrer header. This means you lose analytics data about where your traffic comes from. If your site is on HTTPS, you preserve referrer data from other HTTPS sites.
- Service Workers and PWA: Progressive Web App features like offline caching, push notifications, and the "Add to Home Screen" prompt require HTTPS.
Getting a Free SSL Certificate with Let's Encrypt
You do not need to pay for an SSL certificate. Let's Encrypt provides free, automated, domain-validated (DV) certificates that are trusted by all major browsers. Most hosting providers have integrated Let's Encrypt into their control panels, but if you manage your own server, here is how to set it up with Certbot:
# Install Certbot on Ubuntu/Debian
sudo apt update
sudo apt install certbot
# For Apache
sudo apt install python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com
# For Nginx
sudo apt install python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.comCertbot will automatically configure your web server to use the certificate and set up a cron job for auto-renewal. Let's Encrypt certificates are valid for 90 days, but the auto-renewal process handles this transparently. To verify that auto-renewal is working:
# Test the renewal process (does not actually renew)
sudo certbot renew --dry-run
# Check the renewal timer
sudo systemctl status certbot.timerIf your hosting provider offers one-click SSL through their control panel (SiteGround, Bluehost, HostGator, etc.), use that instead. It is the same Let's Encrypt certificate but managed by your host, which means one less thing to maintain.
Step 1: Update WordPress URLs
Before setting up redirects, tell WordPress that your site now uses HTTPS. Go to Settings > General and update both fields:
- WordPress Address (URL): Change
http://example.comtohttps://example.com - Site Address (URL): Change
http://example.comtohttps://example.com
If you cannot access the admin dashboard (for example, if you are already stuck in a redirect loop), you can set these values directly in wp-config.php:
define('WP_HOME', 'https://example.com');
define('WP_SITEURL', 'https://example.com');Or update the database directly via WP-CLI:
wp option update siteurl 'https://example.com'
wp option update home 'https://example.com'Step 2: Server-Level HTTP to HTTPS Redirect
The redirect must happen at the server level (not in PHP) for two reasons: it is faster because it does not require loading WordPress, and it ensures that all requests (including static files, images, and API calls) are redirected, not just WordPress-handled URLs.
Apache (.htaccess)
Add this at the very top of your .htaccess file, before the WordPress rewrite rules (before the # BEGIN WordPress comment):
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]The R=301 flag sends a permanent redirect, which tells search engines to update their index to the HTTPS version. Do not use R=302 (temporary redirect) because search engines will not transfer link equity to the new URL.
Nginx
In your Nginx configuration, create a separate server block for HTTP that redirects everything to HTTPS:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# ... rest of your configuration
}Using return 301 in Nginx is more efficient than using rewrite because it does not require regex processing.
Step 3: Force HTTPS in wp-config.php
Add these lines to your wp-config.php file, above the "That's all, stop editing!" comment:
// Force SSL for the WordPress admin area
define('FORCE_SSL_ADMIN', true);
// Handle SSL behind a reverse proxy or load balancer
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}The FORCE_SSL_ADMIN constant ensures that the WordPress login page and admin dashboard always use HTTPS, even if someone tries to access them via HTTP directly.
Fixing Redirect Loops Behind Load Balancers and Reverse Proxies
This is one of the most common problems with WordPress HTTPS setups, and it trips up many people. If your site is behind a load balancer, Cloudflare, a reverse proxy, or a CDN, the connection between the proxy and your server is often plain HTTP, even though the connection between the visitor and the proxy is HTTPS. Your server sees an HTTP request and tries to redirect to HTTPS, the proxy sends the request back as HTTP, and you end up in an infinite redirect loop. The browser shows "ERR_TOO_MANY_REDIRECTS."
The fix is the X-Forwarded-Proto header check shown above. The proxy adds this header to tell your server what protocol the original request used. When WordPress sees that the forwarded protocol is HTTPS, it treats the connection as secure and does not trigger a redirect.
If the standard header check does not work, your proxy might use a different header. Check with your hosting provider. Common variations include:
// For some load balancers that use X-Forwarded-Ssl
if (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'on') {
$_SERVER['HTTPS'] = 'on';
}
// For Cloudflare (which also sets X-Forwarded-Proto, but just in case)
if (isset($_SERVER['HTTP_CF_VISITOR'])) {
$visitor = json_decode($_SERVER['HTTP_CF_VISITOR']);
if (isset($visitor->scheme) && $visitor->scheme === 'https') {
$_SERVER['HTTPS'] = 'on';
}
}Step 4: Replace HTTP URLs in the Database
Your WordPress database contains hardcoded HTTP URLs in post content, widget settings, theme options, and serialized data. You need to replace them all with HTTPS versions. The best tool for this is WP-CLI's search-replace command:
# First, do a dry run to see what would change
wp search-replace 'http://example.com' 'https://example.com' --all-tables --dry-run
# If the dry run looks good, run the actual replacement
wp search-replace 'http://example.com' 'https://example.com' --all-tables
# Also handle www/non-www variations if applicable
wp search-replace 'http://www.example.com' 'https://www.example.com' --all-tablesThe --dry-run flag is important. It shows you exactly how many replacements will be made in each table without actually modifying anything. Review the output before running the real replacement. The --all-tables flag ensures that custom tables (from plugins like WooCommerce, ACF, or page builders) are included.
WP-CLI handles serialized data correctly, which is critical. If you try to do a simple SQL find-and-replace, you will break serialized arrays because the string length values will no longer match. Never run a raw SQL REPLACE() query for this purpose.
Fixing Mixed Content Issues
Mixed content occurs when your HTTPS page loads resources (images, scripts, stylesheets, iframes) over HTTP. Browsers block mixed active content (scripts, stylesheets) entirely and may warn about mixed passive content (images). This results in broken functionality or yellow warning icons in the address bar.
To find mixed content issues:
- Browser Console: Open DevTools (F12) and look at the Console tab. Mixed content warnings appear as yellow or red messages listing the exact URLs of the offending resources.
- InspectWP: Run a scan and check the HTML section. InspectWP detects insecure (HTTP) resources loaded on HTTPS pages and lists each one.
- Why No Padlock: The site whynopadlock.com scans your page and reports all mixed content resources with their source URLs.
Common sources of mixed content and how to fix them:
- Hardcoded URLs in theme files: Search your theme's PHP and CSS files for
http://references. Replace them withhttps://or better yet, use protocol-relative URLs (//) or WordPress functions likeesc_url(). - Images in post content: The WP-CLI search-replace command (Step 4) handles most of these. For stubborn cases, check the raw HTML in the Text editor.
- Widgets and Customizer settings: Image URLs in widgets, custom headers, and Customizer settings are stored as serialized data. WP-CLI search-replace handles these correctly.
- Page builder content: Elementor, Beaver Builder, Divi, and other page builders store their content in custom formats. WP-CLI with
--all-tablesusually catches these, but verify by loading pages built with your page builder. - External resources: If you load fonts, scripts, or images from external domains over HTTP, update those URLs. Most CDNs and font services (Google Fonts, cdnjs, etc.) support HTTPS.
Using Really Simple SSL as an Alternative
If the manual approach feels overwhelming, the Really Simple SSL plugin automates most of the process. Install it, activate it, and it will:
- Detect your SSL certificate automatically.
- Update the WordPress Site URL and Home URL to HTTPS.
- Set up a 301 redirect from HTTP to HTTPS via PHP (or .htaccess if possible).
- Fix mixed content by replacing HTTP URLs on the fly.
The downside of Really Simple SSL is that it fixes mixed content dynamically on every page load, which adds a small amount of processing overhead. It is better to fix the root causes (database URLs, hardcoded references) and then remove the plugin. Think of it as a useful bridge during migration, not a permanent solution.
Step 5: Add the HSTS Header
HTTP Strict Transport Security (HSTS) tells browsers to always use HTTPS for your domain, even if the user types http:// in the address bar. After you have confirmed that HTTPS works correctly on your site (no mixed content, no redirect loops), add the HSTS header:
Apache (.htaccess)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;The max-age=31536000 value (one year) tells browsers to remember the HSTS policy for 12 months. Start with a shorter value like max-age=86400 (one day) while testing, then increase it once you are confident everything works. The includeSubDomains directive extends the policy to all subdomains. Only add preload if you plan to submit your domain to the HSTS preload list (hstspreload.org), which hardcodes your domain's HTTPS requirement into browsers themselves.
CDN and Cloudflare HTTPS Configuration
If you use Cloudflare or another CDN, the HTTPS configuration has an extra layer of complexity:
- Cloudflare Flexible SSL: Traffic is encrypted between the visitor and Cloudflare, but the connection between Cloudflare and your server is plain HTTP. This is the easiest to set up (no SSL certificate needed on your server) but provides incomplete security. Data between Cloudflare and your server is unencrypted.
- Cloudflare Full SSL: Traffic is encrypted on both sides, but Cloudflare does not verify your server's certificate. You need an SSL certificate on your server, but it can be self-signed.
- Cloudflare Full (Strict) SSL: The recommended setting. Traffic is encrypted on both sides and Cloudflare verifies your server's certificate. Use a valid Let's Encrypt certificate or a Cloudflare Origin Certificate.
If you switch from Flexible to Full (Strict), make sure your server has a valid SSL certificate installed first. Otherwise, your site will go down because Cloudflare cannot establish a secure connection to your server.
Cloudflare also offers "Always Use HTTPS" under SSL/TLS > Edge Certificates, which performs the HTTP-to-HTTPS redirect at the Cloudflare edge. This is faster than a redirect on your origin server because the visitor never reaches your server for the HTTP request.
Checking SSL Certificate Expiry and Auto-Renewal
An expired SSL certificate is worse than no certificate at all. Browsers display a full-page warning that most visitors will not click through, and your site effectively goes offline. Here is how to monitor your certificate:
# Check certificate expiry date
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Check days until expiry
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -enddateIf you use Let's Encrypt with Certbot, auto-renewal should be configured automatically. But verify it is actually working by checking the Certbot renewal logs:
# Check Certbot renewal log
cat /var/log/letsencrypt/letsencrypt.log | tail -20
# List all certificates and their expiry dates
sudo certbot certificatesFor production sites, set up monitoring that alerts you when your certificate is approaching expiry. Services like UptimeRobot, StatusCake, or even a simple cron job that checks the certificate date can save you from unexpected downtime.
Verify Your HTTPS Setup with InspectWP
After completing the migration, run an InspectWP scan to verify everything is working correctly. InspectWP checks several HTTPS-related aspects:
- SSL/TLS detection: Confirms your site is served over HTTPS.
- Mixed content: Detects any HTTP resources (images, scripts, stylesheets) loaded on your HTTPS pages.
- HSTS header: Verifies the Strict-Transport-Security header is present and configured correctly.
- HTTP redirect: Checks whether the HTTP version of your site properly redirects to HTTPS.
- Security headers: Reviews other security-related headers that complement your HTTPS setup.
If InspectWP reports mixed content warnings, use the browser console to identify the specific resources and fix them using the methods described above. Run another scan after each fix until the report comes back clean.