nginx.confの歩き方。NginxでLaravelを動かすための設定を読み解く

NginxからLaravelを動かす

こんにちは、技術戦略室の安田です。
普段はLaravelやRailsのようなWebフレームワークの実装と改修を担当しています。

先日Nginxの設定を改修しなければ実装できない部分が発生し、通信苦手なんだよなーと思いつつも実装する過程で設定ファイルの読み方や編集するべきポイントを覚えたので、備忘録として残します。

アプリケーションサーバはPHP7.3.1、フレームワークはLaravel8を使用しています。
同じDockerコンテナの中で、NginxとLaravel動かす事を想定しています。

  1. 設定ファイル(nginx.conf)の読み方
    1. httpブロックの設定:基本的な設定。ログ、SSL/TSL、Gzipの設定などをする
    2. serverブロックの設定:Webサーバ(バーチャルサーバ)毎の設定とWebフレームワークに合わせた設定
    3. locationブロックの設定:アクセスされたパス毎に合わせた設定
  2. Nginx と Laravel 連携のまとめ

設定ファイル(nginx.conf)の読み方

Nginxの設定はほとんどの場合 nginx.confと言う名前で作成します。サービスによっては実行環境ごとに変更する為に nginx.prd.conf のようなファイルがあったり、serverブロック毎(バーチャルサーバ毎とも言う)にファイルを分けたりすることもあるかもしれません。

ファイルを分けた場合は `include`を使用することで、外部ファイルの設定をブロックの中に埋めることが可能です。

ブロック」とは中括弧で囲まれた設定部分の事をさします。
それぞれの設定項目のことは「ディレクティブ」と呼びます。

http { # この中は「httpブロック」と呼ぶ
    sendfile on; # sendfileディレクティブ
    tcp_nopush on; # tcp_npushディレクティブ
    ...etc

    server { # この中は「serverブロック」と呼ぶ
        listen 80 default_server; # listen ディレクティブ
        root /cyberowl/public; # root ディレクティブ
        ...etc
    
        location / { # この中は「locationブロック」と呼ぶ
            try_files $uri $uri/ /index.php?query_string; # try_files ディレクティブ
        }
    }
}


もしNginxの設定でわからないことが出た時、Nginxの設定の中のどのブロック部分か一緒に検索すると早く問題が解決できます。

httpブロックの設定:基本的な設定。ログ、SSL/TSL、Gzipの設定などをする

Nginxにもその他のシステムと同じように定石のような設定があります。その大部分をhttpブロックに埋めていきます。

大まかに、通信に関する基本、ログの出し方、SSL/TSL、Gzipに関するディレクティブを記述していきます。
また、ホストなどで枝分かれするserverブロックも記述していきます。

httpブロック以前に必要な設定とeventsブロックについても一緒に記述します

# コア
# 実行ユーザー。php-fpmの listen.owner listern.group と合わせておく
user www-data; 
# workerのプロセス数。数字で指定もできるが、autoでいい
worker_processes auto; 
# プロセスIDを保存する場所を設定
pid /run/nginx.pid
# モジュールを読み込む
# Dockerコンテナを作る際にapt-get nginxで入ってきたもの。環境によってはパスが違う可能性がある
include /etc/nginx/modules-enables/*.conf 

events {
    # 1つのワーカープロセスが同時に処理できる最大接続数。1G
    worker_connections 1024; 
}

http {
    # 基本的な設定
    sendfile on; # ファイルを効率よく送信するAPIを使う。tcp_nopush onとペアで使う。
    tcp_nopush on; # レスポンスヘッダーとファイルを一緒に送信する。効率がいい。

    # TCPコネクションを切断するまでの時間。
    # AWS ALBを使っているならアイドルタイムアウトより大きい数字にしないとエラーが起きる。
    # AWS ALBが切断されたTCPコネクションを利用しようとする為
    # ALBのデフォルトが60秒なのでそれより大きくしておく。
    keepalive_timeout 65;
    types_hash_max_size 2048; # ハッシュテーブルのサイズを設定
    server_tokens off; # Nginxを使っている事を隠す

    # MIMEタイプと拡張子関連付け定義ファイルを読み込む
    include /etc/nginx/mime.types; 
    # 拡張子関連づけ定義を使っても解決できなかった拡張子に対して、
    # ファイルの種類がわからないと言うMIMEを付与する
    default_type application/octet-stream; 
    
    # TLSの設定
    ssl_protocols TLSv1.2; # TLSだけ対応すればいい。TLSv1と1.1は脆弱性がある為、必要に応じて
    # サーバ側指定の暗号スイートを優先する。今回は設定してないのでnginxのデフォルト
    ssl_prefer_server_ciphers on; 
    # AWS ALB => AWS ECS => Nginx と通信がくる場合、
    # SSLの解決をロードバランサがしてくれているので、Docker/Nginxは80番ポートの開放だけになることが多い。
    # そうするとSSLの記述は無用かもしれない(要調査)
    
    # ログの設定
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"'; # ログの見た目。mainと言う名前にする
    access_log /dev/stdout main; # 上で設定したログの見た目を使うための main
    error_log /dev/stdout; # ログをstdout(画面表示)。ファイルに書き出したい時はファイルパスを書く 
    
    # Gzipの設定
    gzip on; # ファイルを圧縮する
    gzip_disable "msie6"; #IE6は圧縮ファイルを開けないので圧縮しない
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # 圧縮するMIME
    
    server { 
        # サーバブロックの設定。次の章で説明
    }
    
}


コア部分は作業環境によって左右される場合があります。

基本的な設定部分はそのままコピペして大丈夫です。どのサービスでも大きく変わることはないです。

TLSの設定に関しては暗号化の方法などを詳しく書いたり、サービスによって変わってくるかもしれません。対応するブラウザやiOSのバージョンなどと相談して設定してください。2021年3月現在、NginxはTLS1.3に対応していません。対応するにはOpenSSLの設定を変更するなど、一手間が必要です。

Gzipの記述は最低限です。環境のメモリ量/CPUに応じて詳細に設定する必要があります。圧縮作業がボトルネックになることもあるので注意しましょう。

serverブロックの設定:Webサーバ(バーチャルサーバ)毎の設定とWebフレームワークに合わせた設定

バーチャルサーバとは、1つのNginxサーバを複数のWebサーバとして扱うことができる技術です。
Nginxにもその機能が入っており、example1.com の時はRailsアプリを表示して、example2.com の時はLaravelアプリを表示したりできます。

listenディレクティブに default_serverを設定すれば、万が一IPアドレスや明示されてないドメインでアクセスされた時に、指定したサーバにアクセスを飛ばします。逆に処理をして欲しくない場合はdefault_serverを記述しないほうがいいでしょう。

http {
    ...

    server {
        listen 80 default_server; # IPでアクセスされた時、こちらのサーバを表示
        server_name example1.com; # example1の時はこちらのサーバを表示
    }
    server {
        listen 80;
        server_name example2.com # example2の時はこちらを表示
    }
}


serverブロックには、使用しているWebフレームワークに正しく情報を渡るように複数の設定が必要です。今回はNginxからLaravelフレームワークが正しく動くための設定を追加していきます。

http {
    ...
    
    server {
        listen 80 default_server;

        # default_serverを設置かつ、1台しかバーチャルサーバがない時
        # あえてserver_nameを書かない事で、全てのアクセスをこのサーバに向けることを明示できる。
        # AWS ALBを使っている場合、ロードバランサ側で処理をすべきのため記述しない
        # server_name example.net www.example.net;

        # ドキュメントルートの設定。
        # これを設定すると、このサーバは設定したパスの配下のファイルを参照する
        # Laravelと連携するには、publicディレクトリのパスを記述する
        # 下の場合は/cyberowl/ディレクトリにlaravelアプリが配置されている事になる
        root /cyberowl/public;
        
        # インデックスの指定
        # laravelにおけるインデックスはpublic/index.phpなので、index.phpを追記する
        index index.html index.htm index.php;
        charset utf-8; # キャラセット。UTF-8でいい
        
        # デバッグの時だけコメントアウトを外して動きを確認する。
        # error_log /dev/stdout debug; 
        
        # Webアプリを動かす為にヘッダー情報を追加する
        add_header X-Frame-Options "SAMEORIGIN" # クリックジャッキング攻撃の防止。他ドメインからiframeでサイトを表示させるのを防ぐ
        add_header X-XSS-Protection "1; mode=block" # XSS攻撃の防止。検知するとレンダリングを停止する
        add_header X-Content-Type-Options "nosniff"; # MIMEタイプのスニッフィングを防止
        
        location / {
            # ロケーションブロックの設定。次の章で説明
        }

    }
}


1番大事なことはドキュメントルート(root)のディレクティブです。LaravelMixなどでコンパイルしたJS、CSS、画像などは publicディレクトリの下に保存するようにしましょう。
静的ファイルにアクセスするためのパスは、ドキュメントルートを / としたパスです。

locationブロックの設定:アクセスされたパス毎に合わせた設定

最後の設定です。
アクセスされたパス毎に処理を書いていきます。大まかに、アクセス全般の処理、静的ファイルにアクセスがきた時の処理、動的ファイルにアクセスがきた時の処理の3つが必要です。

http {
    ...
    
    server {
        ...
        
        # 全てのアクセスに対しての処理。左からドキュメントルート配下を参照する。以下の場合は3つのステップを踏んでいる
        # 1) アクセスがきたパスの静的ファイルが存在するか
        # 2) アクセスがきたパスのディレクトリが存在するか(見つかったらその中の indexを参照する)
        # 3) 上の二つがない場合、index.phpのファイルとする(Laravelに処理を任せる)
        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }
        
        # 静的ファイルの時の処理。正規表現によるパターンマッチングが使える
        location ~* \.(jpg|jpeg|gif|css|png|js|ico|svg)$ {
            expires 30d; # キャッシュの設定。クエリーを変更の処理がしっかりさmaxでもいい。今回は30日
            log_not_found off; # ログを出さない。publicサーバは攻撃に晒されていてログがたくさん出てしまうため
            access_log off; # 同じ理由でアクセスログも見えないようにしている
        }
        
        # 動的ファイルの時の処理
        location ~ \.php$ {
            # PHPをプログラムとして動かしてくれるPHP-FPMのソケットをFastCGIのパスとしてつなげる。
            # メモリに展開させる。FPM側の設定のlistenとパスを合わせる
            fastcgi_pass unix:/cyberowl/storage/php-fpm.sock; 
            fastcgi_index index.php; # FastCGIのindexファイル名。indexと同じものを設定
            
            # PHP-FPMがプログラムを実行するときに必要なパラメータを渡す。サーバ変数のようなもの。
            # パスだけでなく、リクエストヘッダーだったりスキーマ情報だったりを渡している。
            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 
            include fastcgi_params; # その他FastCGI設定ファイルを読み込む
        }
    }
}


3つ設定しました。Laravelのような動的サイトとNginxをつなげる時は、動的ファイルの処理が肝になります。
Nginxの設定だけでなく、PHP-FPMの設定も少し必要になってくるのを覚えておきましょう。

serverブロックに入ってきたアクセスは、locationブロックを全て処理し、パターンにマッチした全ての処理を行います。

たとえphpのついてないパスが来たとしても、try_filesで index.php に処理を任された場合は、動的ファイルの時の処理を行うlocationブロックの処理も行います。

Nginx と Laravel 連携のまとめ

Webサーバに苦手意識を感じていたのですが、1日を使ってやりたいことを実装するまでは理解することができました。

Nginxでできることの一部をAWS アプリケーションロードバランサに任せているのでシンプルな設定で済んでいますが、速度チューニングの実装などになるともっと知識が必要になると思います。(Nginxの時点でApacheより高速ですが)

Railsとの連携方法も調べて見てたのですが、RailsデフォルトのFastCGIのPumaとの連携はLaravelより一癖ありそうなので、別の機会にきちんと覚えようと思いました。


Techブログ 新着記事一覧