[{"data":1,"prerenderedAt":63},["ShallowReactive",2],{"article-7pcqwxb_8jvj":3,"prev-article-7pcqwxb_8jvj":27,"next-article-7pcqwxb_8jvj":45},{"contents":4,"totalCount":25,"offset":26,"limit":25},[5],{"id":6,"createdAt":7,"updatedAt":8,"publishedAt":8,"revisedAt":8,"title":9,"content":10,"tags":11,"is_no_index":24},"7pcqwxb_8jvj","2026-01-26T17:51:45.822Z","2026-01-27T11:53:38.813Z","「おっと、何かがうまくいかなかった。」Ad-Shieldをなんとかしたい","\u003Ch1 id=\"h8d027c8ed3\">はじめに\u003C/h1>\u003Cp>どうも、わたしです。\u003C/p>\u003Cp>わたしのおうちネットワーク環境には少々癖がありまして、なぜか広告配信やユーザーを追跡するトラッキングに関連するFQDN全般の名前解決が、ことごとく失敗するようになっています。これらのドメインから配信されるコンテンツは視覚的にも内容的にも不快なものが多いため、個人的にはこの「不具合」のおかげでセキュリティと精神衛生が守られているのですが、最近では色々なところでAd-ShieldなるAd-Blocker対策用のソリューションが採用され、スクリプト読み込みの失敗を検知して閲覧を拒否されることが増えてきました。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/3aba23b5bd6f4b79800a0305d0e4f8aa/6d68729c516042c0b0f0f0e0b32ecac6/image.png\" alt=\"\" width=\"1919\" height=\"928\">\u003C/figure>\u003Cp>「おっと、何かがうまくいかなかった。」とか言ってくる例のあいつですね。\u003C/p>\u003Ch1 id=\"hab991ce271\">Ad-Shieldの不完全性\u003C/h1>\u003Cp>ただ、このAd-Shieldの挙動を少し掘り下げて確認してみると、その実装品質はお世辞にも高いとは言えません。\u003C/p>\u003Cp>例えばYouTubeの広告検知システムなどは、SSAI技術を含め、コンテンツ配信基盤と密接に統合されており非常に高精度です。誤検知で動画が見られなくなることは稀ですし、そこにはある種の技術的な凄みすら感じます。\u003C/p>\u003Cp>一方で、Ad-Shieldの実装はあまりにも原始的で、クライアントサイドでのスクリプト読み込み成否に過度に依存しています。これが何を意味するかというと、例えば移動中の電車内で通信が不安定なスマホや単に応答速度が遅いパブリックネットワークといった、ごく一般的な環境でインターネットを利用しているだけの順当なユーザーまでもが、無差別に遮断されるリスクがあるということです。\u003C/p>\u003Cp>Ad-Blockerなど一切利用していないのに、たまたまスクリプトの読み込みがタイムアウトしただけで「お前はAd-Blockerを利用しているんだ！！」と決めつけられ、コンテンツを没収される。ユーザー体験としては最悪と言っていいでしょう。\u003C/p>\u003Cp>本来、コンテンツを見てもらうためのWebサイトが、その入り口で正規のユーザーをふるい落として機会損失を生んでいるわけです。品質管理が徹底されたプロダクトとは雲泥の差があり、そもそもまともな技術検証ができている企業であれば、このような粗悪な外部サービスを採用すること自体を躊躇するはずです。\u003C/p>\u003Ch1 id=\"hd7aabd8fbc\">で、どうしよう\u003C/h1>\u003Cp>…と、散々こき下ろしましたが、文句を言っていても画面のオーバーレイは消えてくれません。ただ、この作りの甘さは、裏を返せばとても好都合なことでもあります。\u003C/p>\u003Cp>このシステムの検知ロジックは単純で、そこに付け入る隙があります。通信ログを確認すると、Ad-Shieldは主に\u003Ccode>html-load.com\u003C/code>と、フォールバック用と思われる\u003Ccode>fb.html-load.com\u003C/code>という2つのドメインを使用しています。\u003C/p>\u003Cp>通常は\u003Ccode>html-load.com\u003C/code>からスクリプトを読み込みますが、名前解決に失敗するなどして読み込めないと、即座にフォールバック先の\u003Ccode>fb.html-load.com\u003C/code>へフォールバックする仕様になっているようです。\u003C/p>\u003Cp>ここで残念なのが、サイト側の判定基準です。彼らはどうやら「フォールバックドメインへの接続が成功したかどうか」だけを見て、対策ツールが正常に機能していると判断しているようなのです。実際に広告が表示されているか、DOMが改変されていないかといった本質的な検証は行われていません。\u003C/p>\u003Cp>この挙動を利用すれば、不快なコンテンツの温床である\u003Ccode>html-load.com\u003C/code>の名前解決は失敗させたまま、フォールバック先の\u003Ccode>fb.html-load.com\u003C/code>だけを明示的に許可してあげることで検知を回避できます。\u003C/p>\u003Cp>この環境では\u003Ccode>html-load.com\u003C/code>の読み込みに失敗するため、Ad-Shieldはフォールバック側を読み込みますが、フォールバックドメインの名前解決は許可されているため読み込みに成功します。するとサイト側は「スクリプトが読み込めたから問題ない」と誤認し、警告を解除してコンテンツを表示します。\u003C/p>\u003Cp>もちろん、ユーザー側は広告配信の本体ドメインへの接続に引き続き失敗しているため、画面には広告が表示されないままコンテンツだけが表示されることになります。メインの通信は遮断されているのに、フォールバック側だけが生きているという、明らかに作為的で不自然なネットワーク環境を異常として検出できないあたりに、この技術の悲哀を感じざるを得ません。\u003C/p>\u003Cp>もっとも、この穴も完全に塞がっているわけではないようです。詳細な発生条件までは特定できていませんが、実装が一貫していないおかげで、ごく稀にフォールバックドメイン経由で広告そのものがお漏らしされるケースも観測されています。どうやら純粋な検知用というわけではなく、一部の広告リソースの配信元も兼ねてしまっているのかもしれません。まぁ、許容できる範囲の誤差かと思いますが。\u003C/p>\u003Ch1 id=\"h5f3c23801b\">DNSシンクホールの限界と運用\u003C/h1>\u003Cp>もちろん、DNSレベルでの制御にも限界はあります。あくまでFQDN単位の制御であるため、コンテンツと同じドメインから配信される広告や、直接埋め込まれた広告には無力です。最近はDNSシンクホールを万能なツールのように捉えている方も見かけますが、これは導入が容易で、ある程度の不快なコンテンツが消えればいいという妥協点として運用すべきものです。\u003C/p>\u003Cp>今回の手法もあくまで場当たり的な対策に過ぎません。本来であれば、uBlock Originなどのコンテンツブロッカーを用いてDOMレベルで制御するのがおそらく正しいやり方なのかと思います。現状では\u003Ccode>fb.html-load.com\u003C/code>、あるいは環境によっては\u003Ccode>fb.content-loader.com\u003C/code>といったフォールバックドメインを許可リストに入れることで回避可能ですが、ログに見られる\u003Ccode>report.error-report.com\u003C/code>などはテレメトリ用と思われるため、引き続き名前解決できない状態にしておくのが賢明でしょう。\u003C/p>\u003Ch1 id=\"h1afe451c43\">さいごに\u003C/h1>\u003Cp>今回紹介した手法は、あくまで現時点でのAd-Shieldの仕様を突いたものです。向こうが利用するFQDNを変更したり、検知ロジックを変更したりすれば、すぐに使えなくなるでしょう。所謂いたちごっこに過ぎません。\u003Cbr>しかし、ユーザー体験を損なうようなお粗末な実装でこちらのブラウジングを阻害してくる以上、こちらも快適な環境を維持するために自衛していくしかありません。\u003C/p>\u003Cp>この記事が、同じような「不具合」を抱えるネットワーク環境の管理者の方々にとって、何かの役に立てば幸いです。\u003C/p>",[12,18],{"id":13,"createdAt":14,"updatedAt":15,"publishedAt":14,"revisedAt":15,"slug":16,"name":17},"lte0t59xm8sb","2025-05-02T16:12:38.708Z","2025-12-03T16:06:52.753Z","network","ネットワーク",{"id":19,"createdAt":20,"updatedAt":21,"publishedAt":20,"revisedAt":21,"slug":22,"name":23},"qfey3yw0z1","2025-04-29T08:44:41.687Z","2025-12-03T16:07:14.622Z","technology","テクノロジー",false,1,0,{"contents":28,"totalCount":44,"offset":26,"limit":25},[29],{"id":30,"createdAt":31,"updatedAt":32,"publishedAt":32,"revisedAt":32,"title":33,"content":34,"tags":35,"is_no_index":24,"summary":43},"q08tv15ojb","2026-01-22T12:16:50.171Z","2026-01-22T12:47:05.245Z","個人サイトを、つくろう","\u003Cp>どうも、わたしです。\u003C/p>\u003Cp>最近、色々な方が個人サイトを作ったり、個人ブログを立ち上げるのを見る機会が増えている気がします。Twitterの諸々含め、情勢が不安定なこともあり、避難所として作っていっている感じなんですかね？詳しいことは知りませんが。\u003C/p>\u003Cp>極端な話、一度ドメインを取得しサイトを作ってしまうと、レジストリのポリシにさえ抵触しなければ何をやってもいいわけで。そこには、金儲けアルゴリズムに支配されたSNSとはまた別の、原始的で自由な楽しさがあります。(もちろん、PaaSなんかを使ってホスティングする場合は、その事業者の規約を守る必要がありますが)\u003C/p>\u003Cp>ただ、自由には相応の責任が伴います。企業が提供するプラットフォームとは違い、サーバーが落ちようが、セキュリティホールを突かれようが、全て自分で対処しなければなりません。誰もサポートしてくれないし、復旧もしてくれない。\u003C/p>\u003Cp>だからこそ、そもそもインターネットとは何かというレベル感の話であれば、ここでお伝えすることは何もありません。\u003C/p>\u003Cp>Webは魔法の箱ではなく、堅牢な技術の積み重ねの上に成り立っています。その土台となるネットワークの仕組みも理解せず、うわべだけのツールを触ろうとするのは冒涜に近い。最低限の教養として、そのあたりを自力で理解してから出直してください。話はそれからです。\u003C/p>\u003Cp>さて、そんなことを言いつつ、私もこのサイトを作り始めてから1年弱が経つようです。そろそろ「自分のサイトを作るのはいいぞ」と布教してもいい頃合いかなと思ったので、筆を執るに至りました。\u003C/p>\u003Ch1 id=\"h2bd9968612\">lit.linkへの違和感\u003C/h1>\u003Cp>少し前までだと(今でもかな？)、\u003Ca href=\"http://lit.link\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">lit.link\u003C/a>で各種SNSのリンクをまとめて、プロフィール風のページを作っている人をよく見かけました。\u003C/p>\u003Cp>正直に言いましょう。私はあれが心底嫌いです。もうプロフィールのURL欄にlit.linkのFQDNが見えるだけで、ブラウザバックしたくなるくらい嫌な気持ちになる。\u003C/p>\u003Cp>単にデザインが量産型的だとか、URLがダサいとか、そういう表面的な話ではありません。あれはリンクサービスを謳っておきながら、特定のASNからのリクエストを無条件で拒否するような仕草をしています。DC経由や、一部ISP(国内含む)からのアクセスを一律で弾く。これは例えるなら、「西アフリカ出身者は入店できません」と張り紙をして商売をしているようなものです。Webサービスとしてあまりにも筋が悪い。\u003C/p>\u003Cp>こういった事を言うと、「私は技術者じゃないから知らなかった」「悪気はなかった」という声が聞こえてきそうですが、あえて言いましょう。無知であることを恥じるべきです。\u003C/p>\u003Cp>先天的に学習が困難であればともかく、過度に検閲されていない(比較的)自由なインターネットにアクセスでき、調べるためのツールも、推論するための頭も持っているはずです。それなのに、自分が使っているサービスが何をしているのかを知ろうともせず、便利だからと思考停止で使い続けるのは、単なる知識不足ではなく、怠慢であり、思考の放棄に他なりません。\u003C/p>\u003Cp>そのようなサービスを自己表現の場として選ぶことは、無自覚であったとしても「私は特定層からのアクセスを拒否する差別主義者です」と公言して歩いているのと同義です。\u003C/p>\u003Cp>少しは内省すべきでしょう。\u003C/p>\u003Ch1 id=\"hba2d5583ad\">検索汚染とWordPress\u003C/h1>\u003Cp>さて、気を取り直して「個人サイトをつくろう」という話をしましょう。\u003C/p>\u003Cp>しかし、安易に「個人サイト 作り方」なんてキーワードで検索するのはお勧めしません。上位の検索結果に出てくるのは、お金で殴って安いライターに量産させたような、中身の薄いSEO特化記事ばかりだからです。\u003C/p>\u003Cp>やれレンタルサーバーがどうした、VPSがどうした、結局はWordPressがおすすめという結論。\u003C/p>\u003Cp>はっきり言って、そもそも「個人サイト 作り方」で検索するような層の人間が、WordPressをまともに運用保守できるわけがありません。常識的に考えて、手を出すべきではないでしょう。\u003C/p>\u003Cp>多くの人が使っているという事実は、必ずしもそれが優れていることを意味しません。単に私がWordPressを嫌っているというのもありますが、個人の小規模なサイトにリクエストが来る度、重厚なPHPプロセスを動かしてDBに接続してページを生成して…なんてヘビーで無駄な計算資源を投じているのは、SDGsにも反する行為です。これは極論なので笑って欲しいところですが、あながち冗談でもありません。\u003C/p>\u003Cp>ともあれ、WordPressは構造として古く、脆弱です。コードを見れば、\u003Ccode>require\u003C/code>, \u003Ccode>require_once\u003C/code>が連呼され、globalに大量の\u003Ccode>define()\u003C/code>とコンフィグ用の配列が入り乱れ、サニタイズされているかも怪しい\u003Ccode>$_GET\u003C/code>, \u003Ccode>$_POST\u003C/code>, \u003Ccode>$_REQUEST\u003C/code>が散りばめられている。ファイルの中身はクラス設計どころか、関数にすらなっていない手続き型の記述もかなりある。\u003C/p>\u003Cp>とんでもない。あんなのが令和の時代にWebのデファクトスタンダードとして存在しているのが信じられません。\u003C/p>\u003Ch1 id=\"h87b400ec3b\">どうするの\u003C/h1>\u003Cp>では、具体的にどう構築すべきか。絶対の正解なんてものはありませんが、目的と妥協できるラインによって使い分けるのが賢明でしょう。\u003C/p>\u003Cp>まず、コードが書けない、コストをかけたくない、あるいはCMSなんて大層なものは不要だという場合は、Hugoのような静的サイトジェネレータでHTMLを生成し、S3やR2, Firebase Hosting、あるいはGitHub Pagesあたりに放り込んでおくのが、手堅い選択ではないでしょうか。静的ファイルなら管理の手間もセキュリティリスクも最小限で済みますし。\u003C/p>\u003Cp>一方で、構成にこだわりがあったり、サーバーサイドでの動的な処理が必要な場合。この場合は、Cloud RunやCloudflare Workers、Vercelといったランタイム環境を利用するのが無難です。Dockerコンテナやエッジワーカーとして動かしてしまえば、それなりのスケーラビリティが確保されますし、ローカルとの差異に苦しむことも減るでしょう。現にこのサイトもCloud Run, Cloud Storage, Cloud Load BalancingのGCPスタックに寄せた構成で動かしています。\u003C/p>\u003Cp>そして、どうしてもCMSのような管理画面が欲しいという場合。ここでWordPressに戻っては元の木阿弥です。microCMSやContentfulといったHeadless CMSを採用するとしあわせになれるかもしれません。\u003C/p>\u003Cp>何より、コンテンツとフロントを疎結合に保てるのがとても嬉しい。これなら、将来サイトに破壊的な変更を加えたくなっても、データ資産はそのままでガワだけをサクッと別の技術で作り直すことができます。WordPressの謎めいたテンプレート階層やPHPの作法に、未来永劫縛られ続ける必要はありません。\u003C/p>\u003Cp>ちなみに、このサイトの記事管理もmicroCMSを利用しています。昨年に行われたプラン改定で無料枠の制限が大幅に緩和されたたようで、かなり弄り倒すことができます。小中規模なら、まず使い切れないくらい太っ腹な仕様になりました。かなりおすすめです。\u003C/p>\u003Cp>いずれも共通しているのは、各ベンダが提供する膨大な無料枠をしっかりとしゃぶり尽くせるという点です。旧態依然としたレンタルサーバーに月数百円を払うくらいなら、その分をドメイン代に回した方が建設的でしょう。\u003C/p>\u003Cp>実質的なランニングコストをドメイン維持費だけに抑えつつ、無料のリソースを使い倒す。貧乏人ケチケチホスティングの戦略としては、これが一番無難な選択ではないでしょうか。\u003C/p>\u003Cp>あ、そうそう。ドメインはとりあえずCloudflareで取っておけばいいでしょう。NSこそCloudflareにロックインされることになりますが、気に入らなければ別のレジストラに移管できる自由はありますし、何より、CDNによるキャッシュの恩恵は計り知れません。そのまま乗っかっておくといいでしょう。\u003C/p>\u003Ch1 id=\"h1afe451c43\">さいごに\u003C/h1>\u003Cp>かつてのインターネットは、少しニッチな内容を調べるとすぐ個人のマニアックなブログに当たっていたような気がします。\u003C/p>\u003Cp>しかし、現代ではどうでしょう。GoogleがE-E-A-Tなる高尚(笑)なスローガンを掲げ、情報の品質担保を謳いました。そのアルゴリズムが導き出した答えがこの現状です。\u003Cbr>権威のある企業ドメインなら、中身がコピペだろうが質の低いアフィリエイト記事だろうが優遇され、似たりよったりのキュレーションメディアに埋め尽くされています。\u003C/p>\u003Cp>個人がどれだけ良質なコンテンツを発信しても、ドメインパワーという権威の壁には勝てず、彼方へ追いやられる。Evilに成り果てた現状のGoogleは、もはや個人にとって公平な場とは言い難い状態です。\u003Cbr>愛するかつてのインターネットは、もはや現代の死海文書ではなく、Googleに濾過された消毒臭い資本主義の排水溝になり果ててしまったのかもしれません。\u003C/p>\u003Cp>正直な話、ただ書いたものを誰かに読んでもらって、手っ取り早く承認欲求を満たしたいだけなら、noteやZennで書いた方が絶対にいいかと思います。あそこには最初から読者がいて、プラットフォームがあなたの駄文をおすすめし、拡散してくれます。\u003C/p>\u003Cp>それでもなお、私が個人サイトをおすすめするのは、そこにインターネット本来の自由が感じられるからです。\u003C/p>\u003Cp>誰かのアルゴリズムに媚びる必要もなく、検閲におびえることもない。自分のドメインで、好き勝手なことを書く。そういうインターネットの片隅を感じられる遊びも、悪くないものだと思います。\u003C/p>",[36,42],{"id":37,"createdAt":38,"updatedAt":39,"publishedAt":38,"revisedAt":39,"slug":40,"name":41},"wia4slp55q","2025-04-28T07:14:14.541Z","2025-12-03T16:08:13.798Z","thoughts","独り言",{"id":19,"createdAt":20,"updatedAt":21,"publishedAt":20,"revisedAt":21,"slug":22,"name":23},"どうも、わたしです。最近、色々な方が個人サイトを作ったり、個人ブログを立ち上げるのを見る機会が増えている気がします。Twitterの諸々含め、情勢が不安定なこともあり、避難所として作っていっている感じなんですかね？詳しいことは知りませんが。極端な話、一度ドメインを取得しサイトを作ってしまうと、レジス",51,{"contents":46,"totalCount":62,"offset":26,"limit":25},[47],{"id":48,"createdAt":49,"updatedAt":50,"publishedAt":50,"revisedAt":50,"title":51,"content":52,"tags":53,"is_no_index":24,"summary":61},"qxerlbel9l1j","2026-01-28T14:05:23.242Z","2026-01-29T12:09:47.600Z","TwitterではOGPにWebPが使えないみたい","\u003Ch1 id=\"h8d027c8ed3\">はじめに\u003C/h1>\u003Cp>どうも、わたしです。\u003C/p>\u003Cp>先日、Twitterを何気なく眺めていたときに相互の子がここのURLを投稿してくれているのが目に留まりました。\u003C/p>\u003Cp>自分の書いた記事が共有されているのは嬉しいことですが、その投稿を見て一つ気になる点がありました。本来ならアイキャッチ画像が表示されるはずのOGPが、画像なしのグレーの表示になっていたのです。\u003C/p>\u003Cp>環境の問題かと思い何度かリロードしてみたり、別の端末で確認してみたりしましたが、状況は変わりません。どうやら、私のサイトのOGP設定に何か不具合が起きているようでした。\u003C/p>\u003Ch1 id=\"h1f37091dcb\">WebP移行の落とし穴\u003C/h1>\u003Cp>実はこのサイト、かなり前の改修でパフォーマンス向上を目的として、配信する画像のフォーマットを全面的にWebPへ統一していました。WebPは軽量で取り回しやすく、現代においては事実上のスタンダードと言える形式です。\u003C/p>\u003Cp>その際、主要なブラウザでの表示確認は行っていましたが、TwitterでのOGP表示に関しては特に個別の検証をしていませんでした。というのも、\u003Ca href=\"https://developer.x.com/en/docs/x-for-websites/cards/overview/markup#:~:text=URL%20of%20image%20to%20use,SVG%20is%20not%20supported.\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Twitterの公式ドキュメント\u003C/a>にはサポートされる画像形式としてWebPが明記されていたため、「公式が言ってるんだから大丈夫だろう」と高を括っていたのです。\u003C/p>\u003Cp>おそらく、その改修を行った時点からずっと、Twitter上では画像が表示されない状態が続いていたのだと思われます。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/3aba23b5bd6f4b79800a0305d0e4f8aa/8b9ca34d125749b48cc329940674b108/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202026-01-28%2022.21.16.png\" alt=\"\" width=\"589\" height=\"252\">\u003C/figure>\u003Cp>\u003C/p>\u003Ch1 id=\"h63fb55d34f\">記事ページだけ表示されない\u003C/h1>\u003Cp>状況を詳しく確認してみると、サイト内のすべてのページで画像が表示されないわけではありませんでした。\u003C/p>\u003Cp>サイト内にある\u003Ccode>/\u003C/code>, \u003Ccode>/about\u003C/code>のような固定ページは、Twitter上でも問題なく画像が表示されています。一方で、ブログのメインコンテンツである記事ページやタグページのURLを投稿すると、画像が表示されません。\u003C/p>\u003Cp>特定のページだけが表示されないという挙動から、metaタグの記述ミスではなく、画像ファイルそのものや配信方法に原因があるのではないかと推測しました。そこで、正常に表示されているページと、表示されないページの違いを比較してみることにしました。\u003C/p>\u003Ch1 id=\"h9c1f22bd6d\">原因\u003C/h1>\u003Cp>調査の結果、両者の決定的な違いは画像のファイル形式にありました。\u003C/p>\u003Cp>表示されている固定ページのOGP画像は、サーバー上に配置された静的なPNGファイルを指定していました。対して、表示されない記事ページの画像は、記事ごとにアイキャッチを生成するAPIを経由しており、動的に生成されたWebP画像を指定していました。\u003C/p>\u003Cp>このサイトの仕様として、APIはリクエストに応じてWebP形式で画像を返し、レスポンスヘッダーのContent-Typeも \u003Ccode>image/webp\u003C/code> を正しく設定しています。Webの標準仕様に準拠した実装であり、ブラウザで直接URLを開けば画像は問題なく表示されます。\u003C/p>\u003Cp>\u003Ca href=\"https://github.com/chan-mai/mq1-web/blob/33aabbbebed9ab839ce6d6759753be2cbcd06a3e/server/api/og/article/%5BcontentId%5D.ts#L49-L55\">https://github.com/chan-mai/mq1-web/blob/33aabbbebed9ab839ce6d6759753be2cbcd06a3e/server/api/og/article/%5BcontentId%5D.ts#L49-L55\u003C/a>\u003C/p>\u003Cp>このサイトでは、通常のブラウザ向けにはWebPを標準としていますが、どうやらTwitterのクローラーに対しては、WebP形式の画像は正しく認識されないようです。私のサイト環境においては、動的・静的問わず、WebPを指定している箇所でOGPが表示されるケースはありませんでした。\u003C/p>\u003Ch1 id=\"h9ebabf7874\">公式ドキュメントと実挙動の乖離\u003C/h1>\u003Cp>納得がいかないのは、\u003Ca href=\"https://developer.x.com/en/docs/x-for-websites/cards/overview/markup#:~:text=URL%20of%20image%20to%20use,SVG%20is%20not%20supported.\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Twitter公式のDeveloper Platform\u003C/a>にある\u003Ccode>twitter:image\u003C/code>の仕様記述です。そこには明確にこう書かれています。\u003C/p>\u003Cblockquote>\u003Cp>URL of image to use in the card. Images must be less than 5MB in size. JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an animated GIF will be used. SVG is not supported.\u003C/p>\u003C/blockquote>\u003Cp>「JPG, PNG, WEBP and GIF formats are supported」と、はっきりと書かれているのです。\u003C/p>\u003Cp>しかし現実は、ドキュメント通りにWebPを設定しても表示されません。この現象について相互の子に話を聞いたところ、全く同じ挙動に悩まされた経験があり、結局はWebPを諦めてPNG配信に切り替えることで解決したそうです。個別の環境依存というよりは、プラットフォーム側でWebPへの対応が不完全、あるいは不安定である可能性が高そうです。\u003C/p>\u003Cp>ドキュメント通りの正しい実装をしているにもかかわらず、プラットフォーム側の都合で弾かれてしまうというのは納得しづらい部分ですが、外部サービスに依存している以上、こちらが合わせるしかありません。\u003C/p>\u003Ch1 id=\"hd6164b4a9e\">PNG形式に戻して解決\u003C/h1>\u003Cp>原因がプラットフォーム側のWebP対応状況にあると判断したため、OGP画像の生成ロジックを変更することにしました。\u003C/p>\u003Cp>\u003Ca href=\"https://github.com/chan-mai/mq1-web/blob/8899159ec6a9901fcdc70933b572cf63a4f47ddc/server/api/og/article/%5BcontentId%5D.ts#L49-L55\">https://github.com/chan-mai/mq1-web/blob/8899159ec6a9901fcdc70933b572cf63a4f47ddc/server/api/og/article/%5BcontentId%5D.ts#L49-L55\u003C/a>\u003C/p>\u003Cp>バックエンドの処理を修正し、OGP用画像のリクエストに対しては、PNGを強制して返すように変更しました。本来であればファイルサイズの小さいWebPを使いたいところですが、OGPが表示されないデメリットの方が大きいため、ここはやむを得ない対応です。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/3aba23b5bd6f4b79800a0305d0e4f8aa/91b8bee5f7384b60b7ba80ee88c24a37/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202026-01-28%2022.22.13.png\" alt=\"\" width=\"589\" height=\"430\">\u003C/figure>\u003Cp>修正をデプロイして確認したところ、PNGになった画像はTwitterに認識され、カードが正常に表示されるようになりました💩\u003C/p>\u003Ch1 id=\"h1afe451c43\">さいごに\u003C/h1>\u003Cp>公式ドキュメントには「WebP対応」と明記されているのに、実際には動かない。開発者としてはモヤっとしますが、プラットフォーム側の都合には逆らえません。\u003C/p>\u003Cp>同じようにTwitterのOGP問題で頭を抱えている方の参考になれば嬉しいです。\u003C/p>",[54,55],{"id":19,"createdAt":20,"updatedAt":21,"publishedAt":20,"revisedAt":21,"slug":22,"name":23},{"id":56,"createdAt":57,"updatedAt":58,"publishedAt":57,"revisedAt":58,"slug":59,"name":60},"8q8qyy8sz3","2025-04-29T08:44:23.406Z","2025-12-03T16:07:24.495Z","programming","プログラミング","はじめにどうも、わたしです。先日、Twitterを何気なく眺めていたときに相互の子がここのURLを投稿してくれているのが目に留まりました。自分の書いた記事が共有されているのは嬉しいことですが、その投稿を見て一つ気になる点がありました。本来ならアイキャッチ画像が表示されるはずのOGPが、画像なしのグレ",17,1785520629943]