はじめに
注意事項
- Docker, Next.js, nginxは1ヶ月触った程度の初学者
- あくまで開発中のテスト用の構築
環境
- OS:Ubuntu 20.04.6
- Docker: 26.1.3
- Docker Compose: 2.27.0
- node: 21.7.1
経緯
ブラウザ上でマイクやカメラといった機能を使用したい場合、https通信でアクセスしなければいけません。
localhost
で試している分にはブラウザがよしなに対応してくれたり、Next.jsの--experimental-https
オプションがあるためそこまで困りませんが、スマートフォン上のブラウザから試してみたい場合や、実ネットワークを介して試してみたい場合などはhttps通信でのアクセスが必須です。
そこで、nginxをリバースプロキシとしてNext.jsアプリケーションを配信しつつ、nginxへのアクセスを自己署名証明書を用いたhttps通信に対応させることで、なんちゃってhttps通信を実現します。
実際のWebサイトとして公開する際は当然Let’s Encryptのような認証局を通した証明書を用いる必要がありますが、開発環境として試す際は上記の方がお手軽に試せると思います。
実践編
準備
ディレクトリ構造
今回は以下のようなディレクトリ構造で試します。
- root
- app
- /* create-next-appで作成した各種ファイルとフォルダ */
- Dockerfile
- nginx
- Dockerfile
- nginx.conf
- compose.yaml
appフォルダはcreate-next-app
コマンドで自動的に作成されるため、nginxフォルダだけ作成しておきます。
各種インストール
詳細なインストール方法は割愛しますが、以下をインストールしておきます。
nodeはcreate-next-app
を実行するためにnpxを使用します。
(もしnpxが入っていない場合はnpm install -g npx
でインストールできると思います。)
- node
- dockerとdocker compose
実装
Next.js
今回はとりあえずcreate-next-app
で作成されるサンプルアプリを使用します。
$ npx create-next-app
✔ What is your project named? … app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Next.jsのプロジェクト名をapp、その他のオプションはとりあえずそのままに作成しました。
Docker
上記のcreate-next-app
で作成したappフォルダ、およびnginxフォルダそれぞれにDockerfile
を、プロジェクトのルートディレクトリにcompose.yaml
ファイルを作成していきます。
FROM node:latest
WORKDIR /app
Next.js側のDockerfile
はシンプルにnodeイメージをベースに作成しておきます。WORKDIRで後ほどバインドマウントする/app
フォルダを指定しておきます。
FROM nginx:latest
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y openssl sudo
RUN mkdir -p /etc/nginx/ssl
RUN openssl genrsa 2048 > /etc/nginx/ssl/nginx.key && \
openssl req -subj '/CN=/O=/C=' -new -key /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.csr && \
openssl x509 -req -days 365 -in /etc/nginx/ssl/nginx.csr -signkey /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
COPY ./nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
nginx側のDockerfile
ではnginxイメージをベースに、httpsのために必要となる証明書をopenssl
で作成しています。
また、同じフォルダに作成するnginx.conf
(後述)をDocker上の/etc/nginx/nginx.conf
にコピーします。
services:
app:
container_name: sample_next
build:
context: ./app
dockerfile: Dockerfile
tty: true
volumes:
- ./app:/app
environment:
- WATCHPACK_POLLING=true
ports:
- "3000:3000"
command: sh -c "npm install && npm run dev"
nginx:
container_name: sample_nginx
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- "80:80"
- "443:443"
depends_on:
- app
最後にこれら二つのDockerを起動するためのcompose.yaml
ファイルを作成します。
Next.jsのDockerイメージではcreate-next-app
で作成したapp
フォルダをそのままDocker上の/app
フォルダとして使用できるようにバインドマウントしています。
Next.jsで使用するポート番号をpackage.json
などから変更している場合は適宜書き換えてください。
nginxのDockerイメージではhttp通信で使用される80番ポートとhttps通信で使用される443番ポートを記載しておきます。今回、http通信でアクセスされた際はhttps通信を使用するようにリダイレクトさせたいと思います。
nginx
events {
worker_connections 1024;
}
http {
server {
# http(80番ポート)はhttpsへリダイレクト
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
}
server {
# httpsで使用される443番ポートで待つ
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
# 作成した証明書のファイルを指定
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# Next.jsへ接続
location / {
proxy_pass http://sample_next:3000;
}
}
}
nginxフォルダのnginx.conf
では、以下の二点を記載しています。
- http(80番ポート)でのアクセスは
https://$host$request_uri
へリダイレクト(301) - https(443番ポート)でのアクセスは
proxy_pass
を使用してhttp://sample_next:3000
へ転送-
sample_nextは
compose.yaml
で指定したNext.jsのコンテナ名 -
ssl_certificate
およびssl_certificate_key
にはnginx側のDockerfileで作成した証明書のファイルの場所を指定
-
sample_nextは
余談
nginxが自動で作成したnginx.confに443番ポート周りの設定をしたserverを追記すればいいだろ~と思っていたら、以下の行があるせいでリダイレクトしてくれない、という状況にハマってかなりの時間を浪費してしまいました。include /etc/nginx/conf.d/*.conf;
お気を付けください。
動作確認
Dockerfileをビルドし、起動します。
$ docker compose build
$ docker compose up -d
Dockerを起動したマシンのIPアドレスをブラウザのURL欄に直接打ち込んでみます。
以下ではマシンのIPアドレスを127.0.0.1
に置き換えてあります。
適宜自分の環境と読み替えてください。
すると自動的にhttps://127.0.0.1
にリダイレクトされるはずです。
リダイレクトされた後、初回は以下のような警告がブラウザによって表示されます。(IPアドレス部分は加工しています)
このページの作者である自分が悪意のあるユーザーではないという方は(もちろん全員がそうだと思いますが)、詳細設定から 127.0.0.1にアクセスするをクリックしましょう。
上記のようなNext.jsのサンプルページが表示されればOKです。これでなんちゃってhttps通信でNext.jsアプリケーションへアクセスできるようになりました。
ブラウザ上ではセキュリティ保護なしと表示されてしまいますが、マイクやカメラなどの機能へはアクセスすることができます。
おわりに
nginxを使用することで簡単に自己署名証明書を使用したhttps通信の実現と、Next.jsアプリケーションへの転送を実現することができました。
Let's Encryptなどの認証局の登場によってhttps通信が簡単に実現しやすくなったことは良いことだと思いますが、ブラウザ側もhttp通信を排除しようという動きが進んで、サクッと開発したい時に対応することが増えていくのは難しい点ですね。