CMS・WordPress

【Shifter】静的生成後にテーマのJSだけ404になる原因と解決法|CSS・画像は表示されるのになぜ?

WordPress の静的ホスティングサービス Shifter でサイトをデプロイしたら、CSS や画像は表示されるのに、テーマ内の JavaScript ファイルだけが 404 になる。この記事では、この問題を実際に解決した手順をまとめます。

スポンサーリンク

結論:ShifterURLS::AppendURLtoAll で JS を生成対象に明示的に追加する

先に結論です。テーマの functions.php で Shifter のフィルタ ShifterURLS::AppendURLtoAll を使い、**テーマ内の js/*.js を静的生成の対象 URL に追加**したところ、JS が 200 で返るようになりました。

add_action('init', function () {
  add_filter('ShifterURLS::AppendURLtoAll', function ($urls) {
    // テーマ内の JS ファイルを静的生成の対象に追加
    $js_files = glob(get_template_directory() . '/js/*.js');
    if ($js_files) {
      foreach ($js_files as $file) {
        $urls[] = get_template_directory_uri() . '/js/' . basename($file);
      }
    }
    return array_values(array_unique($urls));
  });
});

以下で、症状・原因の考え方・確認方法を順に説明します。

発生した症状

ローカル環境(Shifter Local)では問題なく読み込まれていた JS が、Shifter で静的生成・デプロイした後の本番環境では 404 になりました。

ファイル ローカル 本番(静的生成後)
css/style.min.css 200 200
img/*.png 200 200
video/*.mp4 200 200
js/main.js 200 404
js/header.js 200 404
js/top.js 200 404
js/axios.min.js 200 404

JS は wp_enqueue_script() で読み込んでおり、HTML には <script src> タグが正しく出力されていました。つまり参照先の URL はあるのに、そのファイルが静的サイト側に存在しない状態です。

環境

  • ホスティング:Shifter(WordPress の静的サイト生成)
  • テーマ管理:GitHub リポジトリ + Shifter Github Plugin/Theme Installer(GitHub Releases の zip からテーマをインストール)
  • CDN:Cloudflare
  • JS/CSS の読み込み:wp_enqueue_script() / wp_enqueue_style()(?ver= クエリは付けない設定)

まず疑ったこと:テーマのディレクトリ名が変わっている

本番の URL を見ると、テーマのディレクトリ名がローカルと違っていました。

ローカル:/wp-content/themes/my-theme/js/main.js
本番  :/wp-content/themes/my-theme-repo/js/main.js

Shifter Github Plugin/Theme Installer でテーマを入れると、GitHub のリポジトリ名がテーマのディレクトリ名になることがあります。そのため最初は「ディレクトリ名が変わったから 404 になった」と考えました。

しかし、これは原因ではありませんでした。

  • 同じ my-theme-repo/ 配下の CSS・画像・動画は正常に読み込めている
  • テーマ内のファイルの URL は get_template_directory_uri() で組み立てているので、ディレクトリ名が変わっても自動で追従する

ディレクトリ名の違いで 404 になるなら、CSS も画像も 404 になるはずです。JS だけが 404 になっている以上、別の原因を探す必要があります。

リポジトリ名やテーマ名を変更して合わせる必要はありません。

原因:JS が静的生成の対象から漏れていた

Shifter は WordPress の URL を収集し、その URL を順に取得して静的ファイルとして書き出します。どの URL を書き出すかは、Shifter が WordPress に入れているヘルパー(shifter-artifact-helper)が一覧を作っています。

今回の JS は、この生成対象の一覧に含まれず、静的サイト側にファイルが書き出されていなかったと考えられます。CSS や画像が問題なかった一方で JS だけが漏れた詳しい理由までは特定できていませんが、生成対象に明示的に追加することで解決しました。

解決方法:ShifterURLS::AppendURLtoAll フィルタで JS を追加する

Shifter には、生成対象の URL を追加するための公式フィルタ ShifterURLS::AppendURLtoAll があります。

公式ドキュメント上の条件

公式ドキュメントによると、このフィルタで追加する URL には次の条件があります。

  • WordPress サイト内の URL であること(外部 URL は取得できない)
  • home_url() 配下の URL であること
  • 末尾がスラッシュ、または許可された拡張子で終わること

許可されている拡張子は .html .xml .rss .rdf .atom .css .js .json です。JS ファイルはそのまま追加できます。

実装コード

テーマの functions.php に次のコードを追加します。

/*
 * Shifter の静的生成でテーマ内の JS が出力されない対策。
 * js/ 配下の .js を生成対象 URL に明示的に追加する。
 * get_template_directory_uri() で組み立てるため、
 * テーマのディレクトリ名がローカルと本番で違っても問題ない。
 */
add_action('init', function () {
  add_filter('ShifterURLS::AppendURLtoAll', function ($urls) {
    $js_files = glob(get_template_directory() . '/js/*.js');
    if ($js_files) {
      foreach ($js_files as $file) {
        $urls[] = get_template_directory_uri() . '/js/' . basename($file);
      }
    }
    return array_values(array_unique($urls));
  });
});

ポイントは3つです。

  1. glob() で js/ 配下を自動で集める:JS ファイルを追加・削除しても、コードを直す必要がありません。
  2. get_template_directory_uri() で URL を組み立てる:テーマのディレクトリ名に依存しません。
  3. array_unique() で重複を除く:ほかの処理で同じ URL を追加していても安全です。

すでに ShifterURLS::AppendURLtoAll でページネーションなどの URL を追加している場合は、同じフィルタの中に JS の処理を足すと、生成対象の管理が1か所にまとまって見通しがよくなります。

サブディレクトリがある場合

js/lib/*.js のように階層がある場合は、RecursiveDirectoryIterator を使って再帰的に集めるか、glob() のパターンを追加してください。

$patterns = ['/js/*.js', '/js/*/*.js'];
foreach ($patterns as $pattern) {
  foreach (glob(get_template_directory() . $pattern) ?: [] as $file) {
    $urls[] = get_template_directory_uri() . str_replace(get_template_directory(), '', $file);
  }
}
スポンサーリンク

反映されたかの確認方法

1. ローカルで生成対象の一覧を確認する

Shifter Local では、次の URL で生成対象の一覧を JSON で確認できます。

curl -sk "https://127.0.0.1:8443/?urls" | jq '.items[] | select(.link_type == "from_filter_hook")'

追加した JS が "link_type": "from_filter_hook" として出ていれば、生成対象に入っています。一覧はページに分かれていて、結果の finished が false なら続きがあります。その場合は ?urls=1 ?urls=2 のように番号を増やして確認してください(最初のページが 0 の扱いです)。

WordPress の管理画面の Shifter > URL Preview でも、同じ一覧を表で確認できます。コマンドを使わずに確かめたいときはこちらが手軽です。

2. 本番で Generate → CDN パージ → ステータス確認

  1. Shifter でサイトを Generate(静的生成)してデプロイ
  2. Cloudflare などの CDN のキャッシュをパージ
  3. JS の URL が 200 で返るか確認
curl -sI https://example.com/wp-content/themes/my-theme-repo/js/main.js | head -1
# HTTP/2 200

CDN のパージは必ず行ってください。 以前の 404 がキャッシュされたままだと、修正が効いていても 404 が返り続けます。

それでも404になる場合のチェックリスト

チェック項目 確認方法 見るべきポイント
テーマの zip に JS が入っているか GitHub Releases の zip を展開 js/ フォルダがあるか。Actions で zip を作る場合は対象ファイルの指定も確認
.gitignore で JS を除外していないか git check-ignore -v js/main.js 何も出力されなければ除外されていない
生成対象に入っているか ?urls エンドポイント、または Shifter > URL Preview from_filter_hook として出ているか
生成物に含まれているか Shifter 管理画面から Artifact をダウンロード wp-content/themes/<テーマ名>/js/ があるか
CDN がキャッシュしていないか CDN のパージ後に再確認 パージ前後でステータスが変わるか
home_url と site_url がずれていないか 設定 > 一般 違う場合、テーマの URL が home_url() 配下から外れて追加できないことがある

生成物(Artifact)に JS がなければ生成側の問題、あれば配信側(CDN など)の問題と切り分けられます。

補足:?ver= クエリを出さない設定について

wp_enqueue_script() の第4引数(バージョン)を指定すると、main.js?ver=1.0.0 のようにクエリ文字列付きの URL が出力されます。今回の環境では、バージョンに null を渡してクエリを出さないようにしています。

CDN はクエリ文字列を含めた URL ごとにキャッシュを持つことが多いため、同じファイルに複数の URL があると、パージの対象も増えます。URL を1つに揃えておけば、更新時は Generate とパージだけで済みます。

wp_enqueue_script('main-js', get_template_directory_uri() . '/js/main.js', array(), null);

その代わり、ファイルを更新したときは Generate → CDN パージ でキャッシュを更新する運用にしています。

よくある質問

Q. テーマのディレクトリ名をローカルと本番で揃える必要はありますか?

A. いいえ。URL を get_template_directory_uri() で組み立てていれば、ディレクトリ名が違っても自動で追従します。ハードコードしたパスがある場合だけ修正が必要です。

Q. CSS や画像も同じ方法で追加できますか?

A. CSS(.css)は許可された拡張子なので追加できます。画像や動画の拡張子は、公式ドキュメントの許可リストには含まれていません。

Q. コードを書かずに追加する方法はありますか?

A. あります。WordPress の管理画面の Shifter > Settings にある Custom URLs に、1行に1つずつ URL を書けば、同じフィルタを通して生成対象に追加されます。URL は https:// から始まる完全な形で書いてください。/js/main.js のような書き方は、home_url() 配下かどうかの判定で除外されます。

ただし、JS ファイルを増やしたり名前を変えたりするたびに、ここを手で書き直す必要があります。テーマの js/ 配下を自動で集められる点で、コードで追加する方法のほうが保守しやすくなります。

Q. フィルタを init の中で登録しているのはなぜですか?

A. 公式ドキュメントのサンプルコードが init アクション内で登録する書き方になっているため、それに合わせています。

まとめ

  • Shifter でテーマの JS だけ 404 になるのは、静的生成の対象から JS が漏れていることが原因と考えられる
  • テーマのディレクトリ名がリポジトリ名に変わっていても、get_template_directory_uri() を使っていれば原因ではない
  • ShifterURLS::AppendURLtoAll フィルタで js/*.js を生成対象に明示的に追加すれば解決できる
  • 反映後は Generate → CDN パージ を忘れずに行う

同じ症状で困っている方の参考になれば幸いです。

参考

スポンサーリンク