🐡
Next.jsで `--experimental-https` フラグをつけて開発するときに気をつけたいこと
ハマったので共有します。
--experimental-httpsフラグとは
nextjsをhttpsで立ち上げることができる。利用している認証サービスがhttpsを要求するときに、便利。
next dev --experimental-https
こちらの記事が詳しい。
問題点
httpなバックエンドサーバーと通信するときにエラーが発生する。
Access to fetch at 'http://localhost:3000/api/xxx' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
当初、バックエンドサーバー側のCORSの設定かと思ったが違った。ブラウザの仕様の問題だった。
流れ:
- ブラウザがhttpsアプリケーションからhttpリクエストが発火すると、httpsにリダイレクトしようとする
- preflight reqestはリダイレクトできない
- 上記のエラーが発生
ブラウザがhttpをhttpsにリダイレクトさせるのを止めるのは難しそうなため、バックエンドサーバー側もhttps化すればよい。
バックエンドサーバーをhttps化する
next.jsを--experimental-https
フラグで立ち上げると、自己署名証明書まわりをよしなにやってくれる。今回はその設定を利用してバックエンド側もその署名を指定することで、https化する。
流れ:
- バックエンドサーバーのレポジトリにシェルスクリプト
add-cert-env
を作成-
CERT_DIR
にnext dev --experimental-httpsで指定されたファイルに書き換える - すると、証明書をbase64エンコードされたものが、'.env'に追加される
-
- シェルスクリプト
add-cert-env
を実行 - バックエンドサーバー側で、証明書を書き換え
- Next.jsからhttpsでリクエストできるようになる
add-cert-env
#!/bin/bash
# 証明書と秘密鍵のあるディレクトリを指定
# これは next dev --experimental-httpsによって生成されたものを指定する
CERT_DIR="../web/certificates"
# もし該当ファイルがなければエラーを出力して終了
if [ ! -f $CERT_DIR/localhost-key.pem ] || [ ! -f $CERT_DIR/localhost.pem ]; then
echo "証明書または秘密鍵が見つかりません。先にpnpm web devを実行して証明書を生成してください。"
exit 1
fi
# base64エンコードして環境変数として`.env`ファイルに追加
echo "SSL_KEY=$(cat $CERT_DIR/localhost-key.pem | base64)" >> .env
echo "SSL_CERT=$(cat $CERT_DIR/localhost.pem | base64)" >> .env
# 完了メッセージ
echo ".env ファイルに SSL_KEY と SSL_CERT が追加されました。"
main.ts
async function bootstrap() {
if (process.env.NODE_ENV === 'development') {
if (!process.env.SSL_KEY || !process.env.SSL_CERT) {
throw new Error(
'pnpm api add-cert-envで自己署名証明書を作成してください',
);
}
}
const httpsOptions =
process.env.NODE_ENV === 'development'
? {
key: Buffer.from(process.env.SSL_KEY!, 'base64'),
cert: Buffer.from(process.env.SSL_CERT!, 'base64'),
}
: undefined;
const app = await NestFactory.create(AppModule, {
httpsOptions,
});
app.enableCors({
origin: process.env.CORS_URL ? process.env.CORS_URL.split(',') : undefined,
allowedHeaders:
'Origin, X-Requested-With, Content-Type, Accept, Authorization',
credentials: true,
});
await app.listen(process.env.PORT || 3000);
}
bootstrap();
Discussion