Docker+Laravel+Next.jsでlocalhostの代わりにカスタムドメインを使う

はじめに

通常ローカル環境で開発する際、仮想サーバーのURLは「http://localhost:8000」のようなものになる場合が多いですが、自分はプロジェクト単位でcookieを分けたいなどの理由で、localhostの部分を独自のドメインにカスタマイズして開発することが多いです。

方法としては、hostsファイルにドメインの割り当ての設定を書いて、Docker側はdocker-compose.yml内でカスタムドメインをエイリアスとして設定するというものです。

しかし今回、Next.jsを使った開発にあたってLaravel(API)側と相互に通信する場合に、上記の設定だけではCORS(Cross-Origin Resource Sharing)のエラーで通信ができなかったので、エラー解消方法も含めてメモしておきます。

前提

  • Windows環境(Docker Desktop for Windows)
  • Laravel8、Next.js共にインストール済み
  • localhostドメインで正常に通信ができている

あと、LaravelとNext.jsの連携を簡単に実装できる「Laravel Breeze – Next.js Edition」を使って認証機能の実装を行っています。

カスタムドメインを使う方法

例えば、カスタムドメインとして「custom.local」を設定してみます。

まず先に、hostsファイルの編集とdocker-compose.ymlの設定など、各環境に合わせて「custom.local」でつながるように設定しておきます。

hostsファイルの設定

例えば、hostsファイルに以下のように追記します。

 127.0.0.1 custom.local

hostsファイルの場所は、「C:\Windows\System32\drivers\etc」など

Dockerの設定

Dockerの設定はいろいろあるかもしれませんが、自分は、docker-compose.ymlに以下のように書いてます。

# 前後略
services:
  nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports:
      - 8000:8000
    depends_on:
      - api
    volumes:
      - ./api/:/var/www/html
# カスタムドメインのエイリアス設定↓
    networks:
      default:
        aliases:
          - custom.local
# 前後略

LaravelとNext.jsの設定

続いて、バックエンドとフロントエンドでそれぞれカスタムドメインを設定します。

「localhost」と設定されている箇所を全て「custom.local」に変更すれば正常な通信ができるばずです。Laravel(バックエンド)側とNext.js(フロントエンド)側の.envでそれそれバックエンドURL、フロントエンドURLの設定をしていると思うので、そこを「custom.local」に変更します。

Laravel側の.env

APP_URL=http://custom.local:8000
FRONTEND_URL=http://custom.local:3000
SANCTUM_STATEFUL_DOMAINS=custom.local:3000

実装の方法によって項目は変わるかもしれませんが、自分の場合は上記3項目設定しています。ポートは環境に合わせて指定してください。

ここでポイントになるのが「SANCTUM_STATEFUL_DOMAINS」です。
sanctumインストール時にconfigファイルで、statefulの設定がデフォルトでしてあるので通常は必要ないはずなのですが、ポートを含むドメインの指定が必要っぽくて、明示的に「SANCTUM_STATEFUL_DOMAINS」を設定する必要があるようです。

Next.js側の.env.local

 NEXT_PUBLIC_BACKEND_URL=http://custom.local:8000

envでは、バックエンドのドメインしか設定できません。

フロントエンドつまり自分自身のドメインはどこで設定するのかですが、ここがわからずCORSエラーが出ていました。サーバー起動時のデフォルトが「localhost」なので、これを変えるには自分でサーバーの設定を書く必要がありました。サーバーの設定はserver.jsで行います。

server.jsの作成

server.jsを新規に作成して、プロジェクトフォルダ直下に保存します。これを使ってフロント側のサーバーを起動します。

中身は、公式ドキュメントのCustom Serverの欄から引用。

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const hostname = 'custom.local' // ←ここ
const port = 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    createServer(async (req, res) => {
        try {
            // Be sure to pass `true` as the second argument to `url.parse`.
            // This tells it to parse the query portion of the URL.
            const parsedUrl = parse(req.url, true)
            const { pathname, query } = parsedUrl

            if (pathname === '/a') {
                await app.render(req, res, '/a', query)
            } else if (pathname === '/b') {
                await app.render(req, res, '/b', query)
            } else {
                await handle(req, res, parsedUrl)
            }
        } catch (err) {
            console.error('Error occurred handling', req.url, err)
            res.statusCode = 500
            res.end('internal server error')
        }
    }).listen(port, (err) => {
        if (err) throw err
        console.log(`> Ready on http://${hostname}:${port}`)
    })
})

6行目のhostnameに「custom.local」を設定してます。他の部分は必要に応じて変更してください。

const hostname = 'custom.local'

package.jsonの編集

サーバー起動コマンドを打った時に、先程作成したserver.jsから起動するように設定します。

  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js",
  },

devstartをそれぞれ変更しています。

これで、フロントエンド側のドメインが「custom.local」になりました。

まとめ

以上で、localhostをカスタムドメインに変更でき、双方の通信も正常にできるようになっているはずです。

今回はなかなかレアケースの記事でしたが、ネットに情報が少なく苦労したのでメモしておきます。もし同様に悩まれている方がいたら参考にしてもらえたらと思います。