2026年10月8日

2026年10月8日

WordPressのCDNエラーを解決する方法

はじめに

WordPressにCDNを設定したのにブラウザのネットワークタブで確認するとオリジンサーバーから直接配信されている・CDN導入後に管理画面のログインがループする・CloudflareのProxy機能を有効にするとWordPressの画像が混在コンテンツ(Mixed Content)エラーになる・Amazon CloudFrontを設定したがwp-login.phpやwp-adminがキャッシュされてログインできなくなったといった問題は、CDNのURL書き換え設定の不備・HTTPSとHTTPの混在・キャッシュ除外ルールの設定漏れが原因です。

症状・原因

  • CDN設定後にサイトのCSSやJavaScriptが読み込まれずデザインが崩れる
  • wp-adminやwp-login.phpへのアクセスがCDNにキャッシュされてログインできない
  • Cloudflare経由でアクセスするとHTTPSなのにブラウザが「安全でない」と表示する
  • CDNのURLに書き換わらず画像が元のサーバーから配信され続けている

解決手順

ステップ1:CDNの設定状態を診断する

# WP-CLIでサイトURLとCDN設定を確認
wp eval "
echo 'siteurl: '   . get_option('siteurl') . PHP_EOL;
echo 'home: '      . get_option('home') . PHP_EOL;
echo 'WP_CONTENT_URL: ' . WP_CONTENT_URL . PHP_EOL;

// アップロードURLの確認
\$uploads = wp_upload_dir();
echo 'Upload URL: ' . \$uploads['baseurl'] . PHP_EOL;
"

# 静的ファイルのレスポンスヘッダーを確認
wp eval "
\$response = wp_remote_get(get_theme_file_uri('style.css'), ['sslverify' => false]);
if (!is_wp_error(\$response)) {
    \$headers = wp_remote_retrieve_headers(\$response);
    echo 'CF-Cache-Status: '   . (\$headers['cf-cache-status']   ?? 'none') . PHP_EOL;
    echo 'X-Cache: '           . (\$headers['x-cache']           ?? 'none') . PHP_EOL;
    echo 'Via: '               . (\$headers['via']               ?? 'none') . PHP_EOL;
    echo 'Content-Encoding: '  . (\$headers['content-encoding']  ?? 'none') . PHP_EOL;
}
"

# HTTPS混在コンテンツを確認
wp eval "
global \$wpdb;
\$http_urls = \$wpdb->get_var(
    \"SELECT COUNT(*) FROM {\$wpdb->posts}
     WHERE post_content LIKE '%http://%'
     AND post_status = 'publish'\"
);
echo 'HTTP URL in posts: ' . \$http_urls . PHP_EOL;
"

ステップ2:WordPressのURLをCDNに書き換える

// wp-config.php: CDN URLを設定
define('WP_CONTENT_URL', 'https://cdn.example.com/wp-content');

// または functions.php でフィルターを使う方法
add_filter('wp_get_attachment_url', 'replace_with_cdn_url');
add_filter('the_content',           'replace_with_cdn_url');
add_filter('script_loader_src',     'replace_with_cdn_url');
add_filter('style_loader_src',      'replace_with_cdn_url');

function replace_with_cdn_url(string $url): string {
    $site_url = get_option('siteurl');
    $cdn_url  = 'https://cdn.example.com';  // CDNのURL

    // 管理画面では置換しない
    if (is_admin()) {
        return $url;
    }

    return str_replace($site_url, $cdn_url, $url);
}
# Mixed Content(HTTP/HTTPS混在)を一括修正
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' \
    --all-tables \
    --skip-columns=guid

# CDN URLに一括書き換え(メディアライブラリのURL)
wp eval "
\$uploads = wp_upload_dir();
echo 'Checking upload URLs...' . PHP_EOL;
\$atts = get_posts(['post_type' => 'attachment', 'posts_per_page' => 5]);
foreach (\$atts as \$a) {
    echo wp_get_attachment_url(\$a->ID) . PHP_EOL;
}
"

ステップ3:Cloudflareの設定を最適化する

// functions.php: Cloudflare対応設定

// ① Cloudflare経由のIPアドレスを正しく取得
add_action('init', function(): void {
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
    }
});

// ② CloudflareのHTTPSをWordPressに認識させる
add_action('init', function(): void {
    if (
        isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
        && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'
    ) {
        $_SERVER['HTTPS'] = 'on';
    }
    // Cloudflare Flexible SSL(オリジンはHTTP)の場合
    if (isset($_SERVER['HTTP_CF_VISITOR'])) {
        $cf_visitor = json_decode($_SERVER['HTTP_CF_VISITOR'], true);
        if (isset($cf_visitor['scheme']) && $cf_visitor['scheme'] === 'https') {
            $_SERVER['HTTPS'] = 'on';
        }
    }
});
# .htaccess: Cloudflare対応(wp-config.phpの前に追加)
# HTTPS判定をCloudflareのヘッダーから取得
SetEnvIf X-Forwarded-Proto "https" HTTPS=on
// wp-config.php: プロキシ経由のHTTPS設定
// Cloudflare・ロードバランサー対応
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}
define('FORCE_SSL_ADMIN', true);

ステップ4:CDNのキャッシュ除外ルールを設定する

// functions.php: CDNキャッシュをWordPressのフックで制御

// ① 投稿更新時にCDNキャッシュをパージ
add_action('save_post', function(int $post_id): void {
    if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
        return;
    }

    $permalink = get_permalink($post_id);

    // CloudflareのCache Purge API
    $cf_zone_id = defined('CF_ZONE_ID') ? CF_ZONE_ID : '';
    $cf_api_key = defined('CF_API_KEY') ? CF_API_KEY : '';

    if ($cf_zone_id && $cf_api_key) {
        wp_remote_post(
            "https://api.cloudflare.com/client/v4/zones/{$cf_zone_id}/purge_cache",
            [
                'headers' => [
                    'Authorization' => "Bearer {$cf_api_key}",
                    'Content-Type'  => 'application/json',
                ],
                'body' => wp_json_encode(['files' => [$permalink]]),
            ]
        );
    }
});

// ② 動的ページにCache-Controlヘッダーを設定
add_action('send_headers', function(): void {
    // ログインユーザーやカート/チェックアウトページはキャッシュしない
    if (is_user_logged_in() || is_cart() || is_checkout()) {
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Surrogate-Control: no-store');  // CDNへの指示
        return;
    }

    // 静的なアーカイブページは1時間キャッシュ
    if (is_archive() || is_home()) {
        header('Cache-Control: public, max-age=3600, s-maxage=86400');
        return;
    }

    // 個別投稿は10分キャッシュ
    if (is_singular()) {
        header('Cache-Control: public, max-age=600, s-maxage=3600');
    }
});

ステップ5:CDNのパフォーマンスを検証・最適化する

# CDNからの配信を確認(curlでヘッダーを取得)
wp eval "
\$test_urls = [
    get_theme_file_uri('style.css'),
    content_url('/uploads/2024/01/sample.jpg'),
];
foreach (\$test_urls as \$url) {
    \$response = wp_remote_head(\$url, ['sslverify' => false]);
    if (!is_wp_error(\$response)) {
        \$headers = wp_remote_retrieve_headers(\$response);
        echo \$url . PHP_EOL;
        echo '  CF-Cache: '    . (\$headers['cf-cache-status']  ?? 'N/A') . PHP_EOL;
        echo '  Cache-Control: ' . (\$headers['cache-control']  ?? 'N/A') . PHP_EOL;
        echo '  Age: '          . (\$headers['age']             ?? '0') . ' sec' . PHP_EOL;
    }
}
"
// functions.php: CDN経由の場合のみリソースURLを最適化
add_filter('wp_resource_hints', function(array $hints, string $relation_type): array {
    $cdn_domain = 'cdn.example.com';

    if ($relation_type === 'preconnect') {
        $hints[] = ['href' => "https://{$cdn_domain}"];
    }
    if ($relation_type === 'dns-prefetch') {
        $hints[] = ['href' => "//{$cdn_domain}"];
    }
    return $hints;
}, 10, 2);

注意事項

  • Cloudflareの「Full (strict)」SSLモードを使用する場合、オリジンサーバーにも有効なSSL証明書が必要です。「Flexible」モードではオリジンはHTTPのままですが、WordPressがHTTPSリクエストとして認識しないためログインループが発生することがあります
  • CDN URLに書き換えたあとはプラグインの設定画面に保存されているURLも変わる可能性があります。バックアップを取ってから変更してください
  • wp-login.php・wp-cron.php・wp-admin/・xmlrpc.phpは必ずCDNのキャッシュ除外ルールに追加してください。キャッシュされるとサイトが正常に動作しなくなります

まとめ

WordPressCDNエラーの解決は①レスポンスヘッダーのCF-Cache-Status/X-Cache確認・wp_upload_dir()でアップロードURLチェック・HTTP混在コンテンツのカウント、②WP_CONTENT_URL定数またはフィルターで静的ファイルURLをCDNに書き換え・wp search-replaceでHTTP→HTTPS一括変換、③HTTP_CF_CONNECTING_IPでIPアドレス取得・HTTP_X_FORWARDED_PROTOでHTTPS認識・FORCE_SSL_ADMIN=trueで管理画面をHTTPS強制、④Cloudflare API経由の投稿更新時キャッシュパージ・send_headersフックで動的ページにCache-Control: no-store・静的コンテンツに適切なs-maxage設定、⑤wp_resource_hintsでCDNドメインへのpreconnect・dns-prefetch追加・レスポンスヘッダーでCDN配信を確認の手順で解決します。

お気軽にご相談ください

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