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=truewp 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を設定する手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ