PowerCMS Xをnginx + php-fpm環境で動作させるための設定を作成しました。2018年12月に私のブログで公開した情報を2023年1月に検証・アップデートしたものです。
nginxの設定を書く目的は以下の2点です。
- 拡張子が.phpへのアクセスをphp-fpmで処理させるため(=ミドルウェアの設定)
- nginxは通常.htaccessを解釈しないことから、PowerCMS Xで必要なリライト処理をnginxの設定に書く必要があるため(ダイナミック生成やライブプレビュー等使用時)
前提
- PowerCMS Xのコードは
/var/www/vhosts/example.com/app
に配置する - RESTfulAPIを利用可能にする
- 次の場合は
/pt-view.php
でダイナミック生成を行う- Cookieに
pt-live-preview
を名前とする情報がある場合(LivePreviewプラグイン) - HTTPリクエストメソッドが
GET
以外の場合 - クエリストリングがある場合
- Cookieに
-
pt-view.php
以外のPHPを「サイト・パス」に配置しても実行しない - 3.〜4.に該当しない場合は次のように処理をする
- 静的ファイルが存在する場合はそのファイルを配信する
- 静的ファイルが存在しない場合は
/pt-view.php
でダイナミック生成を行う
nginx設定例
server {
server_name example.com;
root /var/www/vhosts/example.com/public_html;
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
location /app {
alias /var/www/vhosts/example.com/app;
location ~ /app/(?!api|assets|plugins|theme|index\.php|pt-.*\.php|favicon).* {
return 404;
}
location ^~ /app/api {
fastcgi_split_path_info ^/app/api/(.+?\.php/)?(.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/vhosts/example.com/app/api/index.php;
fastcgi_param REDIRECT_URL $fastcgi_path_info;
fastcgi_pass unix:/run/php-fpm/www.sock;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_buffering off;
fastcgi_intercept_errors off;
fastcgi_pass unix:/run/php-fpm/www.sock;
}
}
location / {
set $use_ptview 0;
if ($http_cookie ~* "pt-live-preview") {
set $use_ptview 1;
}
if ($request_method !~ "GET") {
set $use_ptview 1;
}
if ($query_string ~ "=") {
set $use_ptview 1;
}
if ($use_ptview) {
rewrite ^ /pt-view.php last;
}
index index.html;
try_files $uri $uri/ /pt-view.php;
}
location ~ pt-view\.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php-fpm/www.sock;
}
# プラグインのappディレクトリにあるプログラムを利用する例
location = /pt-members.php {
rewrite . /app/plugins/Members/app/pt-members.php last;
}
}
気を付けるポイント
管理画面のPHP設定では以下に気を付けます。
- fastcgi_buffering off;
- fastcgi_intercept_errors off;
上記がオンの場合、PowerCMS X システムチェック(pt-check.php
)で「システム出力バッファをフラッシュ(flush())することができませんでした。」の警告が表示されました。また、php-fpmのpm(process manager)の設定値によっても表示されることがありました。
検証
検証環境
- Amazon Linux 2
- nginx 1.22.1
- PHP 8.0.25
- PowerCMS X 3.x
- API・LivePreviewプラグインを有効化
- LivePreviewプラグインの「挿入するHTML」に
<mt:livepreviewdate format_ts="Y-m-d H:i:s">
を含むHTMLを設定
検証のためにexpose_php = On
のままにしてあります。検証終了後はOff
にします。
検証内容
-
/app/index.php
で管理画面が表示されること -
/app/pt-check.php
で出力バッファのフラッシュに関するエラーが表示されないこと -
https://example.com/app/api/index.php/v1/0/entry/list
(RESTfulAPI)で記事一覧JSONが表示されること - 記事ページを開くと表示されること(ステータスコード
200
・レスポンスヘッダーにx-powered-by: PHP/8.x.y
がないこと) - 記事ページのURLに
?test=1
を付けて開くと表示されること(ステータスコード200
・レスポンスヘッダーにx-powered-by: PHP/8.x.y
があること) - システムスコープのライブプレビューを設定後に記事ページを開き、設定した日時が表示されること
- ワークスペースのライブプレビューを設定後(システムスコープと異なる日時にする)に記事ページを開き、設定した日時が表示されること
- URLマップでファイル出力を「ダイナミック」にしたページが表示されること
-
/app/config.json
にアクセスできないこと(pt-check.php
でも確認できる)
[非公式] pt-view.phpのカスタマイズ
※PowerCMS X ver.3.2から同等の機能が製品に実装されました。
環境変数「dynamic_ignore_scope」を追加しました。ダイナミック・パブリッシングでpt-view.phpのworkspace_idを考慮せずにルート相対パスからURLを解決します。
2023年1月現在、非公開ページのプレビューやLivePreviewプラグイン使用時などダイナミック・パブリッシングが必要な場合はワークスペース毎にpt-view.php
と.htaccess
を用意する必要があります。しかしnginxでは通常.htaccess
が利用できないためワークスペース毎にlocation
ディレクティブを使用して設定を追記していく必要がありますがそれは手間なため、一つの/pt-view.php
でまかなうことができないか検討してみました。その結果pt-view.php
(Bootstrapper)のテンプレートをカスタマイズすることで実現できました。
カスタマイズした部分を抜粋して掲載します。
if ( $app->request_method != 'GET' || $app->query_string ) {
$app->force_dynamic = true;
}
// ここからカスタマイズ
require_once '<mt:var name="application_dir">/db-config.php';
require_once '<mt:var name="application_dir">/lib/PADO/class.pado.php';
$pado = new PADO();
$pado->prefix = 'mt_';
$pado->colprefix = '<model>_';
$pado->init();
$request_uri = $app->base . $app->request_uri;
$terms = [
'url' => $request_uri,
'delete_flag' => [ 'IN' => [ 0, 1 ] ],
];
$args = [
'sort' => [
'delete_flag' => 'ascend',
'id' => 'descend'
],
];
$urlinfos = $pado->model( 'urlinfo' )->load( $terms, $args, 'id,workspace_id' );
if ( $urlinfos ) {
$workspace_id = (int) $urlinfos[0]->workspace_id;
}
// ここまでカスタマイズ
require_once LIB_DIR . 'Prototype' . DS . 'class.PTViewer.php';
これで先に記述した検証内容が正しく動作することを確認していますが、URLオブジェクトの状態によっては上手くいかないURLがでるかもしれません。また、いずれはカスタマイズすることなく一つのpt-view.php
で済むように改良されているかもしれません。