nginx+apache+php-fpmで作るWebサーバー2024年版

nginx + apache + php-fpmで作る2024年版Webサーバー

2024年現在、Webサーバーはnginxが最もメジャーなサーバーとなっているようですね。
弊社のローカルネットワーク上にある開発環境もapacheから脱皮すべく、nginxをベースにすることになりましたが、現在運用中のサイトで使われているphpコードや.htaccessが全く使えなくなってしまう環境への移行はまだ時期尚早かと思います。
著名なレンタルサーバーがnginxオンリーではなく、nginx + apacheであるために、nginxで調整してしまうと一般的なレンタルサーバーでは問題が発生すること間違いなし。

ということで、nginx + apache + php-fpmサーバーを作成し、バーチャルホストのconfigでどのように処理するかを設定してあげることで混在状態にあるwebサーバーの仕様に合わせやすいようにします。

このサーバーを構築するにあたり、大きく躓いたところが4つあります。

  1. サーバー起動時、apacheのサービス起動速度が遅く、nginxが起動してきたときにバックエンドで受けるapacheのポートが見つからないのでnginxが正常起動しない。
  2. デーモン起動時に名前解決が必要な記述を行うと、条件によってはnginxが起動できずエラーとなる。
  3. 構築したサーバーにiOSでhttpsアクセスを行うと、「ページを開けません。ネットワーク接続が切れました。」となる。通信途中でiOSが接続を切ってしまい、表示されない。
  4. mod_rewriteの状況によっては、httpとhttpsのポート間で無限ループに陥ってサイトが表示されない。

OSはRockyLinux9.2です。
データ共有の為のsambaやssh、SELinuxの無効化やfirewalldの停止などの基本設定は初期段階で済ませます。

/var/wwwをsambaで共有し、以下にバーチャルホスト用のデータディレクトリ、confディレクトリ、ログディレクトリ、TLS証明書ディレクトリを作成し、適切なファイルを配置します。

nginx conf: /var/www/conf.d/(virtualhostdomain).nginx.conf
apache conf: /var/www/conf.d/(virtualhostdomain).apache.conf
HTML: /var/www/vhosts/virtualhostdomain(バーチャルホストごとにディレクトリ作成)
ログ : /var/www/log/virtualhostdomain(バーチャルホストごとにディレクトリ作成)
TLS証明書 : /var/www/tls/以下にワイルドカード自己署名証明書

apache、nginx、php-fpmのインストールは先達の良い資料が有りますので、それを参考にします。
また、明記はしておりませんけども、設定変更後はデーモンの再起動を行ってくださいね。

[root@server ~]# httpd -v
Server version: Apache/2.4.57 (Rocky Linux)

[root@server ~]# nginx -v
nginx version: nginx/1.25.3

[root@server ~]# php -v
PHP 8.3.2 (cli) (built: Jan 16 2024 13:46:41) (NTS gcc x86_64)

[root@server ~]# mariadb
Server version: 11.2.2-MariaDB-log MariaDB Server

各ミドルウェアをインストールし、サーバー起動時に自動起動するようにします。

この時点では、まだバーチャルホストの設定が入っていないので、サービス起動時のエラーは出てきませんが、バーチャルホストの設定がいくつが入ってくると、受けるポートが無いとなること確実なので、まずはapacheが起動していないとnginxが起動しない、apache必須の指定をnginxの起動スクリプトに記述します。
また、各バーチャルホスト設定時にバックエンドに投げるURLの書き方でも起動エラーの原因になります。

[root@server ~]# vim /usr/lib/systemd/system/nginx.service

[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
#最後に httpd.service を追加
After=network-online.target remote-fs.target nss-lookup.target httpd.service
Wants=network-online.target
#新規追加でhttpd.service を追加
Requires=httpd.service

そして、先達の資料通りに nginxはhttpに80番とhttpsに443番、バックエンドで動くapacheにはhttpは8080番、httpsは8443番を割り当て、その他設定はお好みに行います。

[root@server ~]# vim /etc/httpd/conf/httpd.conf

Listen 80
↓
Listen 8080

#バーチャルホストconfディレクトリを追加
IncludeOptional /var/www/conf.d/*.apache.conf
[root@server ~]# vim /etc/httpd/conf.d/ssl.conf 

Listen 443 https
↓ 
Listen 8443 https

以下を /etc/nginx/nginx.confのhttpセクションに追記します。
proxy_hide_headerはiOSからアクセスしなければ問題ないのですが、様々なデバイスへの対応をチェックする開発環境なので必要かと思います。
SSL/TLS証明書は開発用ローカルドメインのワイルドカードゆえに大元で設定しています。
ドメインごと別に指定したいときは各バーチャルホストのconfigに記述すればOKです。

[root@server ~]# vim /etc/nginx/nginx.conf

http {
   	# バーチャルホストconfディレクトリを追加
   	include /var/www/conf.d/*.nginx.conf;

  	# 最大ボディサイズ
	# php.ini post_max_size と同じ値
	client_max_body_size 256M;
	client_body_buffer_size  2M;

	# iOSがTLS接続を切ってしまうことへの対策 
	proxy_hide_header Upgrade;
	
	# TLS証明書はご自分の環境に合わせて
	ssl_certificate /var/www/tls/local.crt;
	ssl_certificate_key /var/www/tls/local.key;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

	# proxy動作時のバッファ量
	proxy_temp_path /var/www/tmp/nginx; 
	proxy_max_temp_file_size 4096m; 
	proxy_buffer_size 16k;
	proxy_buffers 16 16k; 
	proxy_busy_buffers_size 32k;

	# php-fpm動作時のバッファ量
	fastcgi_buffer_size 16k;
	fastcgi_buffers 64 16k;
	fastcgi_busy_buffers_size 32k;
	fastcgi_temp_file_write_size 32k;

}

ここで、PHP-FPM動作時のバッファ量が既定値のままだとほぼ間違いなくwordpress管理画面でcgiキャッシュ不足となり、ディスクへの書き込みが発生します。

2024/02/14 11:03:06 [warn] 3739#3739: 1 an upstream response is buffered to a temporary file /var/cache/nginx/fastcgi_temp/1/00/0000000001 while reading upstream, client: 192.168.*.*

色々パターンを試してみましたが、自社環境ではfastcgi_buffers を例の程度に大きくすれば書き込みは発生しない模様です。
環境によるかと思いますので、適切な量を探してください。

次に、nginxとapacheがそれぞれphp-fpmを使えるようにします。
デフォルトのままだと、apacheユーザーがソケットを所有しており、nginxユーザーがphp-fpmに投げるソケットが有りませんのでnginx用のphp-fpmソケットを用意します。

/etc/php-fpm.d/以下にある www.confをコピーし、
/etc/php-fpm.d/apache.conf
/etc/php-fpm.d/nginx.conf
を作り、元のwww.confはwww.conf.orgなどにリネームして起動しないようにします。

/etc/php-fpm.d/apache.confはプール名ソケット名をwwwからapacheに変更します。
/etc/php-fpm.d/nginx.confはプール名とソケット名、ユーザーとグループをwwwからnginxに変更します。

各configに関して詳しい方は突き詰めて設定していただいても全然問題ありませんが、今回は分けて動けばいいのでファイルの該当文字列wwwの部分をエディタで検索してapacheもしくはnginxに置換する程度です。
例としてapache.confの主に重要な部分を記述します。
nginx.confはapacheの文字列をnginxに置き換えて記述してください。
ついでに、php-fpmのエラーログをドメインごとに吐き出せるように設定を変えておきます。

[root@server ~]# vim /etc/php-fpm.d/apache.conf

[www⇒apache]
user = apache
group = apache
listen = /run/php-fpm/www.sock ⇒ /run/php-fpm/apache.sock
;pmの設定値は開発用なのでスピードより安定を重視
pm = static
# pm.max_chlidrenはCPUコア数
pm.max_children = 4
pm.max_requests = 100
;以下、ドメインごとにエラーログを出力させるために変更できない既定値を解除し、代わりの既定値をセット
;php_admin_value[error_log] = /var/log/php-fpm/www-error.log
;php_admin_flag[log_errors] = on
php_value[error_log] = /var/log/php-fpm/apache-error.log
php_flag[log_errors] = on

apacheのPHPソケットはphp.confの最終部分にwww.sockとして記述されていますので、ここをプール名をapacheとしたapache.confに揃えます。nginxユーザーのソケットは後述しますがバーチャルホストの設定ファイルに記述します。

[root@server ~]# vim /etc/httpd/conf.d/php.conf

SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
↓
SetHandler "proxy:unix:/run/php-fpm/apache.sock|fcgi://localhost"

php-fpmのエラーログですが、これ、デフォルトだと先のコンフィグに有ったように/var/log/php-fpm以下に出てしまいます。
通常は、.user.iniでログの出力先を指定しますが、これをこのまま本番環境にアップしてしまうことは必ず発生してしまうので、開発環境は別のファイルを参照するようにして設定値が本番環境に干渉しないようにします。

/etc/php.iniを変更して、.local.user.iniというファイルを参照するようにします。

[root@server ~]# vim /etc/php.ini
;;;;;;;;;;;;;;;;;;;;
; php.ini Options ;
;;;;;;;;;;;;;;;;;;;;
; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini"
;user_ini.filename = ".user.ini"
user_ini.filename = ".local.user.ini"

これで、個々のサービスの関係が築かれたので干渉せずに起動するはずです。

サービス関係の設定が終わったので、バーチャルホストの設定に入ります。

ここでは、各バーチャルホスト設定ごとにapache用とnginx用のコンフィグファイルを用意します。弊社が使用するVPSサーバーはPleskを使用しているので、Pleskのデフォルトに合わせておくとRewriteなどを追加する際に書き換えずに済みます。
domainの部分は設定するドメインが分かりやすいように適宜変更願います。
また、開発用という観点からHSTSの時間は長すぎるとテストの際に面倒が起きますので普段の運用ではありえない5分で設定しています。

[root@server ~]# vim /var/www/conf.d/domain1.nginx.conf

#nginxですべて処理する例
#-- domain1.local.cretbird.co.jp ---------------------------------------------
server {
  listen 80;
  server_name domain1.local.cretbird.co.jp;
  #httpアクセスはnginxでhttpsに転送
  return 301 https://domain1.local.cretbird.co.jp$request_uri;
}

server{
	listen 443 ssl;
	http2 on;
	server_name domain1.local.cretbird.co.jp;
	root /var/www/vhosts/domain1.tld;
	index index.php index.html index.htm;
	access_log  /var/www/log/domain1.tld/ssl_access.nginx.log main;
	error_log  /var/www/log/domain1.tld/ssl_error.nginx.log warn;
	add_header Strict-Transport-Security 'max-age=300; includeSubDomains';

	location ~ \.php$ {
		include /var/www/conf.d/php-fpm.inc;
	}
	
# NGINX rewrite文をインクルード	
	include /var/www/vhosts/domain1.tld/rewrite.inc;
	

}

ここで、各セクションで同じ設定値を共用しているのでソースの冗長化を防ぐために共通部分を別ファイルにしてincludeします。
includeしているphp-fpm.incの内容は、

fastcgi_pass unix:/run/php-fpm/nginx.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

rewrite.incの内容は

#開発サーバーとPlesk環境で共通の Nginx Rewrite
if ($request_uri ~ "^./index.html$"){ 
  rewrite "^/(.)/index.html$" /$1/ permanent;
}
if ($request_uri ~ "^./index.php$"){ 
  rewrite "^/(.)/index.php$" /$1/ permanent;
}

として、適宜追加します。

この内容をPleskのNginx追加設定にペーストするか、直接インクルードしてしまってもOKです。

include /(web root ディレクトリ)/rewrite.inc;

次の例はwordpressなど .htaccess が複雑な場合に用いるとすんなり移行できます。
http⇒httpsリダイレクトをnginxでするとリダイレクト無限ループになる場合があるので、.htaccessでのhttp⇒httpsリダイレクトに任せます。
.htaccessを修正しても良いかと思いますが、現行本番サーバーと異なる値となってそのままアップしてしまえば事故の元です。
本当ならnginxの443番はapacheの8080番に投げれば暗号化処理をしないで済むのでさらに高速になるんですが、強制https動作するようwordpressなどの設定値を変える必要があり本番環境と違う設定になる可能性が高くなります。

[root@server ~]# vim /var/www/conf.d/domain2.nginx.conf

#wordpress動作に適したconfig例
#-- domain2.local.cretbird.co.jp ---------------------------------------------
server {
  listen 80;
  server_name domain2.local.cretbird.co.jp;
  root /var/www/vhosts/domain2.tld;
  index index.html;
  access_log /var/www/log/domain2.tld/access.nginx.log main;
  error_log /var/www/log/domain2.tld/error.nginx.log debug;
  #静的ファイルは直接処理(wordpressで拡張子をhtmlにするプラグイン等拡張子コントロールに注意)
  location ~ .*\.(html?|jpe?g|gif|png|svg|webp|css|js|inc|ico|inc|eot|ttf|woff|woff2|swf|mpg|mp4)$ {
    break; # apache に投げない
  }  
  #NGINX + APACHE&PHP-FPM
  location / {
    proxy_pass http://localhost:8080;
    include /var/www/conf.d/proxy_set_header.inc;
  }
}

server{
  listen 443 ssl;
  http2 on;
  server_name domain2.local.cretbird.co.jp;
  root /var/www/vhosts/domain2.tld;
  index index.html;
  access_log /var/www/log/domain2.tld/ssl_access.nginx.log main;
  error_log /var/www/log/domain2.tld/ssl_error.nginx.log debug;
  add_header Strict-Transport-Security 'max-age=300; includeSubDomains';
  #静的ファイルは直接処理(wordpressで拡張子をhtmlにするプラグイン等拡張子コントロールに注意)
  location ~ .*\.(html?|jpe?g|gif|png|svg|webp|css|js|inc|ico|inc|eot|ttf|woff|woff2|swf|mpg|mp4)$ {
    break; # apache に投げない
  } 

  #NGINX + APACHE&PHP-FPM
  location / {
    proxy_pass https://localhost:8443;
    include /var/www/conf.d/proxy_set_header.inc;
  }
}

includeしているproxy_set_header.incの内容は、

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

また、proxy_pass でサーバー名をlocalhostや127.0.0.1以外のDNSへの問い合わせが必要なサーバー名にすると、DNSとの通信が出来ない状況や名前解決できないときにnginxが起動できずエラーとなってしまいます。
実運用サーバでは、hostsに記述することで回避できます。
弊社ではローカル開発環境専用DNSを立てており、このDNSサーバーの起動が遅く、始業時に用意!ドン!で電源ボタンを押したらWebサーバーが先に立ち上がってDNSでの接続先の名前解決が出来ずにnginxだけが起動できなかったのです。

次はバックエンドapacheの設定です。
domain1.tld はnginxだけで動作しているので、バックエンドのapacheは設定不要です。
nginxでhttp⇒httpsのリライトを掛けていないのでこちらも両方受けることが出来るようにして、.htaccessに処理を任せます。

[root@server ~]# vim /var/www/conf.c/domain2.apache.conf

# wordpress動作に適したconfig例
# -- domain2.local.cretbird.co.jp ---------------------------------------------
<VirtualHost *:8080>
	ServerName domain2.local.cretbird.co.jp
	DocumentRoot /var/www/vhosts/domain2.tld
	DirectoryIndex index.php index.html
	ErrorLog /var/www/log/domain2.tld/error.apache.log
	CustomLog /var/www/log/domain2.tld/access.apache.log realip
</VirtualHost>
<VirtualHost *:8443>
	ServerName domain2.local.cretbird.co.jp
	DocumentRoot /var/www/vhosts/domain2.tld
	DirectoryIndex index.php index.html
	Protocols h2 http/1.1
	ErrorLog /var/www/log/domain2.tld/ssl_error.apache.log
	CustomLog /var/www/log/domain2.tld/ssl_access.apache.log realip
	SSLEngine on
	SSLProtocol -all +TLSv1.2 +TLSv1.3
	SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
	SSLCertificateFile /var/www/tls/local.crt
	SSLCertificateKeyFile /var/www/tls/local.key
	Header set Strict-Transport-Security "max-age=300"
</VirtualHost>

設定したドメインが表示されるかを確認しますが、たぶんこれではエラーでapacheが起動しないと思います。
nginxからのリクエストを受ける際、IPアドレスがローカルサーバーのIPとなりクライアントIPが記録されないのでCustomLogでrealipという名前を作っています。

[root@server ~]# vim /etc/httpd/httpd.conf

LogFormat "%{X-Real-IP}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" realip
LogFormat "%{X-Forworded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" forworded
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined


ここまで。
適宜その他logrotateの設定をしてくださいね。

これは単機での設定例なので、nginxフロントエンド1台+php-fpm2台+データ用NFSサーバー1台+DBサーバー1台なんて構成も柔軟に出来るかな?なんて思っています。

こちらもどうぞ
ミスリーディングソフト(詐欺まがいソフトにご注意ください。

フリーソフトは機能的で便利な一方、インストールする時にそのフリーソフトとはほぼ関係のないソフトが付いてくるものが結構あり、 その中には何かしらの危険性・迷惑性を持ったソフトも含まれています。

【知らないと損をする】印刷広告・チラシからのWebページへのアクセス数を知ろう!QRコード活用術

DMやチラシを配られている方からよく伺うのは「配ることに意味がある」「配らないと不安だから配る」「配っているけど効果が感じられない」などなど様々です。

Webブラウザのプッシュ通知機能を使った詐欺まがいの行為

最近、パソコンでWebページを閲覧していると、こんな通知を見たことがありませんか? 例えば、乗りものニュース https://trafficnews.jp/ サイトでは以下のような表示が出ます。 例はMozilla Firefoxで、同じようにGoogle [...]

Secured By miniOrange