2時間くらいウンウン唸るはめになったのでメモ。
tl;dr
- DockerでNginxを使う場合は
default.conf
を無視するな - 大抵の場合において不要なので
include
ディレクティブを削除しろ
シチュエーション
TypeScript開発のために,Dockerのnginxオフィシャルイメージでファイル(index.html
とかentrypoint.js
)をホスティングしたい。
nginxの設定は以下の通りにして,docker-composeを用いてコンテナを起動した。
# ...snip... http { # ... snip ... server { listen 80 default_server; listen [::]:80 default_server; location / { root /usr/share/nginx/html; } location ~ ^/js/(.+)\.js$ { root /usr/share/nginx/; } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } }
すなわち,JSファイルだけ別ディレクトリから読み込みたい,という具合だ(ホストの側で別々のディレクトリで管理し,Dockerコンテナにマウントしているという事情がある)。
症状
全くこのlocation設定が効いていない。index.html
は普通に配信されているが,/js/entrypoint.js
をGETしようとすると404が返ってきてしまう……。
正規表現がおかしいのかな?などと勘ぐって調整してみたが全く変化がない。どういうこと??
答え
この状況では,コンテナの80番ポートでnginxがリッスンし,これにホストの8000番ポートをバインドしていた。
version: '3' services: nginx: image: nginx:1.19.0 volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./dist/:/usr/share/nginx/js/:ro - ./html/:/usr/share/nginx/html/:ro ports: - 8000:80
するとhostname
は当然localhost:8000
としてnginxに送られてくる。しかしながら8000番に対応するserver
ディレクティブは存在しないので,デフォルトの設定にフォールバックしてしまい,全てのファイルをデフォルトのファイルパスから読むような動作になっていた。
Q. でもdefault_server
を指定していたんじゃないの?
/etc/nginx/conf.d/default.conf
にはこう書かれている(コメントは削除している):
server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
server_name
ディレクティブには localhost
が指定されているため,優先的にこちらがヒットしてしまったのだった。
どのようにしてリクエストが処理されるか,ドキュメントを見てみよう。
In this configuration nginx tests only the request’s header field “Host” to determine which server the request should be routed to. If its value does not match any server name, or the request does not contain this header field at all, then nginx will route the request to the default server for this port.
つまり,default_server
を必要とするまでもなく,server_name
が合致したのでこちらが選ばれた,というわけ。
対処
http
ディレクティブで指定されていたinclude /etc/nginx/conf.d/*.conf;
を削除するとあっさり直った。
defaultなんだし放置で良いでしょって思ってたら痛い目を見たという話でした。
まとめ
デフォルトが用意されているアプリケーションでは,デフォルト値の吸引力が強い場合があるので気を付けよう!
追記
docker-compose run --rm nginx nginx -T
みたいに実行すると,有効になっているコンフィグが全てstdoutに掃き出されるので活用しよう。
thanks to: id:nabeop