2026年6月17日
2026年6月17日
WordPressのSSL・HTTPS設定エラーを解決する方法
はじめに
WordPressをHTTPSに移行した後にブラウザのアドレスバーに鍵マークが表示されず「保護されていない通信」と表示される・管理画面はHTTPSなのにフロントエンドの画像やスクリプトがHTTPで読み込まれる・SSL証明書の期限が切れてサイトにアクセスできなくなった・Cloudflareを使用しているが証明書の設定が誤っていてリダイレクトループになるといった問題は、データベース内のHTTP URL・Mixed Contentエラー・SSLの設定不備が原因です。
症状・原因
- ブラウザに「このサイトは安全ではありません」や証明書エラーが表示される
- ページのソースコードに
http://から始まる画像・CSS・JSが混在している(Mixed Content) wp-config.phpにSSL設定を追加したがサイトのURLがHTTPのままになっている- データベースの
wp_optionsテーブルにhttp://のURLが残っている
解決手順
ステップ1:SSL設定状態を診断する
# SSL証明書の状態を確認
openssl s_client -connect your-site.com:443 -servername your-site.com 2>/dev/null | \
openssl x509 -noout -dates -subject
# WordPress のURL設定を確認
wp eval "
echo 'siteurl: ' . get_option('siteurl') . PHP_EOL;
echo 'home: ' . get_option('home') . PHP_EOL;
echo 'HTTPS: ' . (\$_SERVER['HTTPS'] ?? 'not set') . PHP_EOL;
echo 'is_ssl(): ' . (is_ssl() ? 'true' : 'false') . PHP_EOL;
"
# データベース内のHTTP URLを確認
wp eval "
global \$wpdb;
\$http_count = \$wpdb->get_var(
\"SELECT COUNT(*) FROM {\$wpdb->posts} WHERE post_content LIKE '%http://your-site.com%'\"
);
echo 'Posts with http URLs: ' . \$http_count . PHP_EOL;
\$option_count = \$wpdb->get_var(
\"SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_value LIKE '%http://your-site.com%'\"
);
echo 'Options with http URLs: ' . \$option_count . PHP_EOL;
"
ステップ2:WordPressのHTTPS設定を修正する
// wp-config.php: HTTPS設定
define('WP_HOME', 'https://your-site.com');
define('WP_SITEURL', 'https://your-site.com');
// 管理画面もSSLを強制
define('FORCE_SSL_ADMIN', true);
// リバースプロキシ経由のHTTPS認識
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
# データベース内のHTTP URLをHTTPSに一括置換
wp search-replace 'http://your-site.com' 'https://your-site.com' \
--skip-columns=guid \
--report-changed-only
# シリアライズされたデータも対応
wp search-replace 'http://your-site.com' 'https://your-site.com' \
--all-tables \
--skip-columns=guid
ステップ3:Mixed Contentエラーを修正する
// functions.php: Mixed Contentの自動修正
// ① コンテンツ内のHTTP URLを自動的にHTTPSに変換
add_filter('the_content', function(string $content): string {
if (is_ssl()) {
$content = str_replace('http://your-site.com', 'https://your-site.com', $content);
}
return $content;
});
// ② スクリプト・スタイルのSRC属性を修正
add_filter('script_loader_src', 'force_https_url', 20);
add_filter('style_loader_src', 'force_https_url', 20);
function force_https_url(string $src): string {
if (is_ssl() && str_starts_with($src, 'http://')) {
$src = str_replace('http://', 'https://', $src);
}
return $src;
}
// ③ Content Security Policy ヘッダーを追加
add_action('send_headers', function(): void {
if (is_ssl()) {
header('Content-Security-Policy: upgrade-insecure-requests');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
}
});
ステップ4:SSL証明書の自動更新を設定する
# Let's Encrypt証明書の状態を確認
certbot certificates
# 証明書を手動で更新
certbot renew --dry-run # テスト実行
certbot renew # 実際に更新
# 自動更新のCronを設定
crontab -e
# 以下を追加(毎日2回チェック)
0 0,12 * * * certbot renew --quiet --post-hook "systemctl reload apache2"
// functions.php: SSL証明書の期限監視
// ① SSL証明書の有効期限を定期チェック
add_action('wp_daily_ssl_check', function(): void {
$host = parse_url(home_url(), PHP_URL_HOST);
$port = 443;
// SSL証明書情報を取得
$context = stream_context_create(['ssl' => ['capture_peer_cert' => true]]);
$client = @stream_socket_client(
"ssl://{$host}:{$port}", $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $context
);
if (!$client) {
error_log('[SSL] Cannot connect to ' . $host . ':' . $port);
return;
}
$params = stream_context_get_params($client);
$cert = openssl_x509_parse($params['options']['ssl']['peer_certificate']);
$expires = $cert['validTo_time_t'] ?? 0;
$days = (int) ceil(($expires - time()) / 86400);
error_log(sprintf('[SSL] Certificate expires in %d days: %s', $days, $host));
if ($days < 30) {
wp_mail(
get_option('admin_email'),
sprintf('SSL証明書の期限切れまで%d日', $days),
sprintf("%s のSSL証明書が%d日後に期限切れになります。\n今すぐ更新してください。", home_url(), $days)
);
}
});
if (!wp_next_scheduled('wp_daily_ssl_check')) {
wp_schedule_event(time(), 'daily', 'wp_daily_ssl_check');
}
ステップ5:HSTS・セキュリティヘッダーを設定する
# .htaccess: HSTSとセキュリティヘッダーを追加
<IfModule mod_headers.c>
# HSTS(1年間HTTPSを強制)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Mixed Content対策
Header always set Content-Security-Policy "upgrade-insecure-requests"
# クリックジャッキング防止
Header always set X-Frame-Options "SAMEORIGIN"
# MIME タイプスニッフィング防止
Header always set X-Content-Type-Options "nosniff"
# リファラーポリシー
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
# HTTPからHTTPSへのリダイレクト
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
注意事項
wp search-replaceでURLを一括置換する前に必ずデータベースのバックアップを取得してください。シリアライズされたデータも含めて置換するため、誤った値で置換すると復旧が困難になる場合があります。--dry-runオプションで事前に変更件数を確認してから実行してください- HSTSを設定した後は、指定した期間中(
max-ageの秒数)はHTTPへの接続が強制的にHTTPSにリダイレクトされます。証明書の問題が発生した場合にHTTPにフォールバックできなくなるため、まず短い期間(例:max-age=86400で1日)でテストしてから本番の1年間に変更してください - Cloudflareを使用している場合、SSL設定は「フル(厳格)」モードを推奨します。「フレキシブル」モードではCloudflareとサーバー間がHTTPになるため、WordPressがHTTPSリクエストを認識できずリダイレクトループが発生する場合があります
まとめ
WordPress SSL修復は①openssl s_clientで証明書の有効期限確認・is_ssl()と$_SERVER['HTTPS']の状態診断・DBのHTTP URL件数確認、②WP_HOME/WP_SITEURLをhttpsに変更・FORCE_SSL_ADMIN=true・wp search-replaceでDB内URLを一括置換、③the_contentフィルターでHTTPリンクを変換・force_https_urlでスクリプト/スタイルのSRCを修正・Content-Security-Policy: upgrade-insecure-requestsヘッダーを追加、④certbot renewの自動更新cron設定・stream_socket_clientでSSL期限を取得し30日前にメール通知、⑤.htaccessにHSTS/CSP/X-Frame-Options/X-Content-Type-Optionsを設定する手順で解決します。