CORSエラーと戦った話

CORSエラーと戦った。

Access to XMLHttpRequest at 'http://~~' from origin 'http://~~0' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

こんなやつ。
Next.jsのプロジェクトで結構無茶なことを行い、結果CORSエラーがでない形で実装したので備忘録として書いていく

共通HTMLを外部から読み込みたい

外部から提供されているHTMLパーツ(CSSjQuery付)をNext.jsのプロジェクトで読み込んでほしいという依頼があった。
SEOのことを考えて、サーバーサイドレンダリング時に読み込みたいらしい。
そうなると、下記手順となる。
①PagesでHTMLを文字列として取得する。
②取得したHTMLをライブラリ(自分はhtml-react-parserを使用した)でDOMにパースし、Document.jsにて埋め込む

404などの静的ページではサーバーサイドレンダリングが行われない

nextjs.org
大体Document.jsに定義すればCSSファイルもjsファイルも読み込んでくれるのだが、
しかし404や500などのエラーページではサーバーサイドレンダリングが行われないため、
Document.jsが読み込まれないのだ。
これでは共通HTMLを読み込むことができない。
そこで最初はapp.jsでfetch定義しようとしたが、ここで表題のCORSエラーにぶち当たることになる

CORSエラーがなぜ今になって発生したのか

サーバーサイドレンダリングでは起きていなかったCORSエラーがなぜ発生するのか。
それはBasic認証の問題やサーバーサイドでの通信かクライアントでの通信かなど、複数条件が絡み合っていた。
Basic認証が共通HTMLの方にかかっていると、サーバー側では一度fetch時に認証するものの、再度クライアント側でも入力を求めてくる。
(別タブで既に認証済みであれば要求されない)
だが、クライアント側では再度の入力を求められず、結果として認証エラー(CORSエラー)が発生するのだ
なのでfetch時のみ認証が通ればよいということではなかった。
これはクライアント(ブラウザ)の挙動によるところがあり、
chromeでは再度の入力をアラートで求めてくるが、safariでは求められないためそのままCORSエラーとなった。
また、サーバー側よりもクライアント側のほうが制約が厳しい。このためサーバーサイドレンダリングではエラーが発生しなかった。

共通HTMLを提供している側にAccess-Control-Allow-Originの設定をしてもらったものの、
Basic認証がかかっているとうまく突破できず、けっきょく*を指定してもエラーを解消できなかった。
(提供側に行ってもらった作業であり、こちらで確認できたわけではない。)

静的ページを使わないことにする

要はクライアント側での通信からエラーが始まっているので、error.jsを作成することにした。
nextjs-ja-translation-docs.vercel.app
ただこの場合、ルーティングがないURLはすべてerror.jsに飛んでしまうため、
ヘルスチェックなどのURLを作成している場合はルーティング定義が必要となる。

まとめ

単純にCORSエラーに対処したという記事ではないが、対処の方法はたくさんあるのであえてクライアント側での通信を避けた経験を記載した。