はじめに
この記事はクライアントから特定のレスポンスヘッダーにアクセスできないバグに関しての記事です。
このバグはフロントエンドにNext.jsを、バックエンドにGoを採用した学校の課題で、本番環境で動作させた時に発生しました。
POSTメソッドのレスポンスとしてLocationヘッダーをセットしたのですが、クライアントから取得することができませんでした。
Swagger UIやネットワークタブからはレスポンスヘッダーの存在を確認できたにも関わらず、なぜか取得できなかったため、原因が中々推測できず30分ほど悩みました。
今後忘れたころにまた遭遇する可能性もありますので、ここで一旦記事にしたいと思います。
先に解消方法から
バグの原因はCORSのAccess-Control-Expose-Headersレスポンスヘッダーの設定ミスでした。
Locationヘッダーはセーフリストに含まれていないため、クライアントからアクセスするにはAccess-Control-Expose-Headersヘッダーに加える必要がありました。
例えば今回採用したGinの場合は、
// 略
- c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
+ c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Location")
// 略
とすることで、クライアントからLocationヘッダーにアクセスすることができるようになりました。
問題の発生状況
POSTメソッドのLocationレスポンスヘッダーに新しいリソースのIDをセットするために、以下のようなコードを書きました。WebフレームワークとしてGinを採用しました。
c.Header("Location", fmt.Sprintf("/%s", output.ID))
c.JSON(http.StatusCreated, nil)
それからしばらくして、本番環境でフロントエンドとバックエンドの疎通を確認することになりました。
Next.jsからAxiosを使ってリクエストを送信し、リソースのIDを取得してページ遷移しようとしたところ、Locationヘッダーの値を取得することができない。というエラーが発生しました。
ネットワークタブのレスポンスヘッダーを確認してみたところ、確かにLocationヘッダーとその値がセットされていました。また、Swagger UI側からも確認してみましたが、当然、Locationヘッダーがセットされていました。
しかしクライアント側から取得することができません。console.logでレスポンスヘッダーをコンソールに出力してみると、Content-Length以外の情報がありませんでした。
Access-Control-Expose-Headersヘッダー
以前の章にもある通り、原因はAccess-Control-Expose-Headersヘッダーでした。
Access-Control-Expose-Headers レスポンスヘッダーは、レスポンスの一部としてどのヘッダーを公開するかを、その名前を列挙して示します。
という内容のようです。
規定では7種類のレスポンスヘッダーが公開されるそうなのですが、この中にLocationヘッダーは含まれていません。
クライアントが他のヘッダーにアクセスできるようにするには、ここに公開するヘッダーを列挙する必要があります。
Access-Control-Expose-Headersヘッダーの構文は以下の通りです。
Access-Control-Expose-Headers: <header-name>, <header-name>,
Access-Control-Expose-Headers: *
まとめ
この記事ではクライアントから特定のレスポンスヘッダーにアクセスできないバグに関する記事をまとめました。