ドキュメントルートのiconsディレクトリが404になる問題を解決する方法

先日、このブログでとある問題が起きていることを発見した。 大したことない内容だと思っていたら原因究明に思いがけず苦労する羽目になったので、 似たような問題に悩んでいる人のために内容を記録しておく。

発生した問題

当ブログのドキュメントルートにはiconsディレクトリができており、 そこにいくつかの大きさのサイトロゴ画像が出力されている。 それらを用いたlinkタグが各ページに吐かれ、apple-touch-iconを設定している。 以下のような感じ。

index.html
<head>
  <!-- ... -->
  <link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="256x256" href="/icons/icon-256x256.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png?v=f4f25ae6de11479a68d13969e08d0198">
  <link rel="apple-touch-icon" sizes="48x48" href="/icons/icon-48x48.png?v=f4f25ae6de11479a68d13969e08d0198">
  <!-- ... -->
</head>

今回起きた問題は、これらのiconsディレクトリ配下の画像が全て404エラーになり、 apple-touch-iconが読み込まれていなかったというもの。

前提

Gatsby Starter Blogベース

このブログはGatsby Starter Blogをベースに作っている。 Gatsby Starter Blogの構成に従い、 faviconやapple-touch-iconはgatsby-plugin-manifestを用いて設定するようにしている。 具体的には以下のような感じ。

gatsby-config.js
module.exports = {
  // ...
  plugins: [
    // ...
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        // ...
        icon: `content/assets/icon.png`,      },
    },

自作のicon.pngを配置したパスをiconで指定してやることで、 faviconと色々な大きさのiconファイルを出力してくれる。

Apacheの共有サーバにホスティング

Gatsbyで生成した静的サイトはNetlifyとかのサービスにホスティングするのが一般的だと思うけど、 このブログは諸々の事情で、今は共有のレンタルサーバに置いている。で、WebサーバはApache。

どうやらこの「共有」の「Apache」という構成が問題だったらしい。

問題の原因

調べてみると、Apacheは初期状態では /icons/へのアクセスは他のディレクトリ内の内容を参照する というAliasが設定されているらしい。 具体的にどのディレクトリかは構成によって異なるとは思うけど、概ね/usr/share/httpd/icons/辺りのよう。

この辺りが参考になった。
Apache2.4セキュリティ「iconsディレクトリ対策」を行い、不要なコンテンツ表示を無効化する

つまり、この初期設定が変えられてない(もしくは別の指定がしてある)場合、 https://ryzca.com/icons/icon-48x48.pngみたいなURLでアクセスしたところで、 実際にはGatsbyが生成してくれたiconsディレクトリは無視され、 全く縁もゆかりも無いディレクトリを参照することになる。 そこには当然icon-48x48.pngというファイルはないので、404エラーが返ってくる。

これが問題の全容。 この結論に辿り着くまでメチャクチャ時間かかった。

解決方法

原因が特定できたため、解決方法は2通りに絞られた。

  • Apacheの設定を変える
  • アイコン格納ディレクトリの名前を変える

それぞれ見ていく。

Apacheの設定を変える

余計な設定があるのだから、その余計な設定自体を消してしまう、 というのが一番手っ取り早いのだけど、ここで「共有」というのが問題になった。

俺は共有サーバを使っている大勢のうちの1ユーザでしかないので、 当然ながらApacheの設定を変えるような権限はない。 それならと、.htaccessでなんとかできないかと調べてはみたものの、 Aliasは.htaccessでは使えないのでこれも不可だった。

つまり、この構成である限りはサーバ側での対応はできない。

アイコン格納ディレクトリの名前を変える

iconsディレクトリ以外なら問題ないわけだから、 格納ディレクトリを別の名前に変えてやるというのが2つ目の方法。

サーバ側の設定にアプリを合わせるというのはすごく不本意だが、 Apacheの設定を変更できない以上この方法を採らざるを得ない。 こういう時にVPSだと便利なんだろうなぁと思うけど、 まぁだいぶ手間を省けてる面もあるわけだし、別にサーバ運用したいわけではないから仕方ない。

ともかく、アイコンのディレクトリはgatsby-plugin-manifestで生成されているので、 その辺りの設定をいじっていく。

gatsby-plugin-manifest設定の変更

調べてみると全く同じ問題に困ってる人がIssueを上げていた。

[gatsby-plugin-manifest] Configurable name for icons folder? #12233

gatsby-plugin-manifestのオプションで以下のように指定すれば、 出力されるアイコンのファイルパスの変更自体はできる。

gatsby-config.js
module.exports = {
  // ...
  plugins: [
    // ...
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        // ...
        icon: `content/assets/icon.png`,
+       icons: [
+         {
+           src: `/favicons/android-chrome-192x192.png`,
+           sizes: `192x192`,
+           type: `image/png`,
+         },
+         {
+           src: `/favicons/android-chrome-512x512.png`,
+           sizes: `512x512`,
+           type: `image/png`,
+         },
+         // サイズごとに作成

ただ、Issueの中にも書かれている通り、 各サイズごとにiconsに追加していく必要があって割と面倒臭い。 置換で一発とはいえ間違いも起こしやすいだろうし、何よりオプション設定が長すぎてウザイ。

どうしようかなと思ってたら、先程のIssueに素敵な解決法が書かれていた。これを丸ごとパクって以下のように設定する。

gatsby-config.js
+ const generateFavicons = (sizes) => {
+   return sizes.map(size => {
+     return {
+       src: `favicons/icon-${size}x${size}.png`,
+       sizes: `${size}x${size}`,
+       type: "image/png",
+     }
+   })
+ }

module.exports = {
  // ...
  plugins: [
    // ...
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        // ...
        icon: `content/assets/icon.png`,
+       icons: generateFavicons([48, 72, 96, 144, 192, 256, 384, 512]),
      },
    // ...

これで、アイコンたちをfaviconsディレクトリに出力する設定をスマートに書くことができた。

あとはビルドとデプロイすれば、アイコンたちがちゃんと読み込まれるはず。

まとめ

サーバ構成を意識する必要があまりないSSGだからこそ問題に気付き難かった。 Gatsbyの静的ファイルホスティングに敢えてこんな構成を使う人は多くないだろうけど、 悩んでハマってる人の役に立てば良いな。


止まらない実行を目指すITエンジニア。

© 2020-2022, Ryz