🐘

Nginxプロキシ設定におけるPHP動作の仕組み

に公開

目的

今回、webサーバーを構築しているなかで、nginxを使用したリバースプロキシの構築を試みました。
https://kamifubuki-ao.com/ へのアクセスはweb1コンテナで、
https://kamifubuki-ao.com/proxy/ へのアクセスはweb2コンテナで行うようにしたいと考えています。
実際に構築してみると、ルートへのアクセスはhtml,phpともに問題なくアクセスできるのですが、/proxy/へのphpアクセスができない状況になってしまいました。

この記事は、この問題を解決したときの備忘録です。

現状

Dockerコンテナ内でNginxとPHP-FPMを構成しており、Nginxをプロキシとして動作させています。
web1コンテナとweb2コンテナそれぞれでnginxを動作させており、php-fpmは1つのコンテナで動作させています。

コンテナ環境

  • app
    • php-fpm
  • db
    • mysql
  • web1
    • nginx
  • web2
    • nginx
  • certbot
    • certbot

それぞれのコンテナの設定

appコンテナ

appコンテナでは9000ポートでphp-fpmを動作させています。

dbコンテナ

dbコンテナではMySQLを動作させています。が、現状compose upした後なにも触れていないです。

web1コンテナ

web1コンテナではnginxを動作させています。
default.confは以下の通りです。

server {
    listen 80;
    listen 443 ssl;
    server_name ${DOMAIN};

    ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
    ## test
    root /app/public;  

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ =404;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /404/index.html;
    
    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_intercept_errors on;
    }
    
    location ~ /\.(?!well-known).* {
        deny all;
    }
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location /proxy/ {
        proxy_pass http://web2:8080/;
    }   
}

こちらは kamifubuki-ao.comへの接続で一番先に呼び出されるサーバーのため、SSL証明書を設定しています。

web2コンテナ

web2コンテナではweb1と同じくnginxを動作させています。
default.confは以下の通りです。

server {
    listen 8080;
    server_name _;

    ## test
    root /app2/public;  

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ =404;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /404/index.html;
    
    location ~ \.php$ {
        fastcgi_pass app:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /app2/public$fastcgi_script_name;
        fastcgi_intercept_errors off;
        add_header X-Debug-Script-Filename /app2/public$fastcgi_script_name;
    }
    
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

こちらの通信は表に出ないため、8080ポートにしてhttpでやり取りしています。

certbotコンテナ

証明書更新用のコンテナです。

何が起きたのか

web1のdefault.confで location /proxy/ {}を設定して前方一致したアクセスはweb2コンテナを呼び出そうとしていましたが、なぜか呼び出されず、404エラーが表示されている状況でした。

理由

これは、nginxの仕組みによるもので、前方一致よりも正規表現のほうが優先されるために起こっていたものでした。
この影響で、 ~ .php$が正規表現のために優先され、web1のドキュメントルートの中から proxy/index.phpを探してしまい、404が表示されてしまっていたのです。

解決策

location /proxy/ {} だったところを、 location ^~ /proxy/ {}に変更することで、正規表現よりもこの条件が優先されるようになり、正常にweb2コンテナが呼び出されるようになりました。

まとめ

^~ をつけると正規表現よりも優先されるため、前方一致を優先させたい等の場合は ^~ をつけるべし

Discussion