Cloudflare Pages で存在しない URL が 200 になる(ソフト404)原因と対処|Astro の 404 ページと独自ドメイン設定
この回でやること
Cloudflare Pages に置いた Astro のサイトで、次の2つを行います。連載「Cloudflare Pages で公開する」の第2回です。
- 存在しない URL にトップページが 200 で返る状態を直し、正しく 404 を返す
- 独自ドメイン(cyber-blow.com)を設定し、https へのリダイレクトと canonical を確かめる
連載の全体は次のとおりです。
- 第1回:Cloudflare Pages にデプロイする
- 第2回:存在しない URL を 404 にして、独自ドメインを設定する(この記事)
- 第3回:microCMS で記事を公開したら自動で再ビルドする
前提として、Astro は静的出力(アダプターなし)で、Cloudflare Pages へのデプロイまでは済んでいるものとします。デプロイの手順は第1回に書いています。
存在しない URL にトップページが 200 で返ってくる
症状
デプロイしたあと、存在しない URL を開いても、エラーにならずにトップページが表示されました。ステータスコードも 404 ではなく 200 です。
原因:404.html が無いと SPA とみなされる
Cloudflare Pages は、出力の直下に 404.html が無いと、そのサイトを SPA(シングルページアプリケーション)だとみなします。そして、存在しないパスにはトップページ(/)を返します。
If your project does not include a top-level 404.html file, Pages assumes that you are deploying a single-page application. This includes frameworks like React, Vue, and Angular. Pages' default single-page application behavior serves the root (/) for any incoming path that does not correspond to a static asset, allowing your application to handle routes like /about or /help client-side from within your SPA.
SPA なら、どの URL でもトップの HTML を返し、ブラウザ側の JavaScript で画面を切り替えるので、この動きが便利です。しかし、ページごとに HTML を生成する Astro の静的サイトでは、存在しない URL にもトップページが返るだけになってしまいます。
なぜ困るのか:ソフト404
ページが存在しないのに 200 を返すと、検索エンジンからは、どの URL にもトップと同じ内容のページがあるように見えます。このように「存在しないページなのに 200 を返す」状態は、ソフト404 と呼ばれます。
Google のドキュメントでは、200 が返ってきても、中身がエラーや空のページを示していると判断した場合は、Search Console にソフト404 エラーとして表示すると説明されています。
参考:How HTTP status codes, and network and DNS errors affect Google Search | Google Search Central
対処:Astro で 404 ページを作る
Astro では、src/pages/404.astro を作ると、ビルドで 404.html が出力されます。
For a custom 404 error page, you can create a 404.astro or 404.md file in src/pages. This will build to a 404.html page. Most deploy services will find and use it.
このサイトの 404 ページは次のとおりです。共通レイアウトを使い、トップページとヘッダーのナビと同じリンクを並べて、迷った人が移動できるようにしています。
---
// 404.html があると、Cloudflare Pages は存在しないURLに 404 を返す
// (無いとトップページを 200 で返してしまい、検索エンジンにソフト404と見なされる)
import { NAV_ITEMS } from '../consts';
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="ページが見つかりません">
<div class="not-found">
<h1 class="page-title">ページが見つかりません</h1>
<p>お探しのページは削除されたか、URLが変更された可能性があります。</p>
<ul>
<li><a href="/">トップページ</a></li>
{
NAV_ITEMS.map((item) => (
<li>
<a href={item.href}>{item.label}</a>
</li>
))
}
</ul>
</div>
</BaseLayout>確認する
デプロイしたあと、存在しない URL に curl でアクセスして、ステータスコードと中身を確かめました。
$ curl -s -o /dev/null -w '%{http_code}\n' https://cyber-blow.com/no-such-page/
404
$ curl -s https://cyber-blow.com/no-such-page/ | grep -o '<title>[^<]*</title>'
<title>ページが見つかりません | cyber-blow</title>ステータスコードが 404 になり、404 ページの中身が返っています。
セクションごとに 404 ページを分けることもできる
Cloudflare Pages は、リクエストされたパスと同じディレクトリから順に上へたどって、いちばん近い 404.html を探します。たとえば /blog/404.html を置けば、/blog/ 以下だけブログ用の 404 ページにできます。
This means that you can define custom 404 paths for situations like /blog/404.html and /404.html, and Pages will automatically render the correct one depending on the situation.
このサイトは、いまのところ /404.html の1つだけです。
独自ドメイン cyber-blow.com を設定する
設定
Pages プロジェクトのカスタムドメインに cyber-blow.com を追加しました。
cyber-blow.com のような www などの付かないドメイン(apex ドメイン)を Pages で使うには、そのドメインが、Pages プロジェクトと同じ Cloudflare アカウントのゾーンになっている必要があります。
To deploy your Pages project to a custom apex domain, that custom domain must be a zone on the Cloudflare account you have created your Pages project on.
確認したこと
設定したあと、次の3つを確かめました。
1つ目は、http でアクセスすると https に 301 でリダイレクトされることです。パスもそのまま引き継がれます。
$ curl -sI http://cyber-blow.com/ | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://cyber-blow.com/
$ curl -sI http://cyber-blow.com/blog/ | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://cyber-blow.com/blog/2つ目は、canonical が https://cyber-blow.com/ から始まる URL になっていることです。
$ curl -s https://cyber-blow.com/ | grep -o '<link rel="canonical"[^>]*>'
<link rel="canonical" href="https://cyber-blow.com/">3つ目は、Pages が最初に用意する <プロジェクト名>.pages.dev の URL でも、canonical が独自ドメインを指していることです。独自ドメインを設定したあとも、pages.dev の URL では同じ内容が見られます。canonical が独自ドメインを指していれば、検索エンジンにはこちらが正規の URL だと伝わります。
$ curl -s https://<PROJECT>.pages.dev/ | grep -o '<link rel="canonical"[^>]*>'
<link rel="canonical" href="https://cyber-blow.com/">canonical はリクエストのホストではなく site 設定から作る
pages.dev でアクセスしても canonical が独自ドメインになるのは、canonical を astro.config.mjs の site から作っているからです。
site: 'https://cyber-blow.com',
// canonical・サイトマップ・内部リンクのURL表記を「/」終わりに統一する
trailingSlash: 'always',const canonical = new URL(Astro.url.pathname, Astro.site);静的サイトなので、HTML はビルドのときに一度だけ作られます。どのホスト名でアクセスされても、canonical は site に書いた URL になります。
まだ残っていること
www.cyber-blow.com
www.cyber-blow.com はまだ設定していません。いまは名前解決できない状態です。
$ curl -sSI https://www.cyber-blow.com/
curl: (6) Could not resolve host: www.cyber-blow.com設定するときに気をつけたいのは、Pages のダッシュボードでドメインを追加する前に、DNS に CNAME レコードだけを手で作ると、522 エラーになることです。
Manually adding a custom CNAME record pointing to your Cloudflare Pages site - without first associating the domain (or subdomains) in the Cloudflare Pages dashboard - will result in your domain failing to resolve at the CNAME record address, and display a 522 error.
別の TLD から .com へのリダイレクト
cyber-blow.net・cyber-blow.site・cyber-blow.online から cyber-blow.com への 301 リダイレクトも、まだ設定していません。Cloudflare のリダイレクトルールで設定する予定です。
まとめ
- Cloudflare Pages は、出力の直下に
404.htmlが無いと SPA とみなし、存在しない URL にトップページを 200 で返す - Astro では
src/pages/404.astroを作れば404.htmlが出力され、正しく 404 が返る /blog/404.htmlのように、ディレクトリごとに 404 ページを分けることもできる- apex ドメインを Pages で使うには、そのドメインが同じ Cloudflare アカウントのゾーンである必要がある
- 独自ドメインの設定後は、http→https の 301、canonical、pages.dev 側の canonical を curl で確かめる
次回は、microCMS で記事を公開したときに、Cloudflare Pages を自動で再ビルドする設定を扱います。