hiroaki@_@

PHP5.1.0以降で発生するDATE関数エラーについて

PHPには、日時を取得するDATE関数というものがあります。
このDATE関数の簡単な使い方として、日時を取得して、指定した日付文字列で返すというものです。
例えば、

<?php echo date('Y'); ?>

とすると、4桁の数字で年が出力されます。(例:2015)
よくWebサイトのコピーライト部分に使用されており、年が変わる度にコピーライト部分の編集をする必要がなくなるので、とても有り難い関数です。

そのDATE関数を使っているWebサイトのApacheエラーログを確認したところ、下記のようなエラーが記録されていました。

[日付] [error] [client 接続元IPアドレス] PHP Warning:  date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in /var/www/html/ファイル名.php on line 88

原因としては、PHP5(PHP5.1.0以降)から仕様変更が行われたようで、デフォルトタイムゾーンを設定せずDATE関数を使用するとエラーが発生するようです。
重大なエラーではありませんが、Apacheのエラーログに大量に記録されたり、間違った日付を取得してしまう可能性があるため、解決するのが一番。そもそもエラーをそのままにしておくのは、良いことではありませんので。

このDATE関数のエラー解決方法は、
「PHPファイル内にdate_default_timezone_set関数を記述する方法」と、
「php.iniにdate.timezoneを設定する方法」の二通りがあります。

PHPファイル内にdate_default_timezone_set関数を設定する方法

とても簡単な方法ですが、DATE関数を使用するPHPファイル内に下記の通り記述します。

date_default_timezone_set('Asia/Tokyo');

これでDATE関数エラーは発生しなくなります。

php.iniにdate.timezoneを設定する方法

php.iniにタイムゾーンを設定することで、date_default_timezone_set関数を使わなくても、DATE関数エラーを解決することができます。

まずは、php.iniの場所を確認します。
php -i でphpinfo()と同じようにPHP情報を閲覧することができます。
また、ファイル内から指定した文字列を検索するgrepコマンドを組み合わせることで、ファイル内の必要な部分のみ表示させることができます。
では、php.iniの場所を確認するので下記のように入力します。

[root@ ~]# php -i | grep php.ini
Configuration File (php.ini) Path => /etc
Loaded Configuration File => /etc/php.ini
PHP Warning: Unknown: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in Unknown on line 0

php.iniの場所が /etc/php.ini にあると確認できました。
次に、php.iniのタイムゾーン設定がどうなっているのか確認します。

[root@ ~]# php -i | grep timezone
PHP Warning: Unknown: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in Unknown on line 0
Default timezone => Asia/Tokyo
date.timezone => no value => no value

「date.timezone => no value => no value」となっていますので、date.timezoneにタイムゾーンの設定がされていないということが確認できました。
それでは、php.iniの中にあるdate.timezoneの設定を行いましょう。
まずは、php.iniを開きます。

[root@ ~]# vi /etc/php.ini

次にphp.iniの中から、下記の項目を探します。

[Date]
; Defines the default timezone used by the date functions
; http://www.php.net/manual/en/datetime.configuration.php#ini.date.timezone
;date.timezone =

「;date.timezone =」の下の行に、「date.timezone = Asia/Tokyo」と入力します。

[Date]
; Defines the default timezone used by the date functions
; http://www.php.net/manual/en/datetime.configuration.php#ini.date.timezone
;date.timezone =
date.timezone = Asia/Tokyo

php.iniの編集は、以上で終了です。編集を終了して、ファイルを上書き保存してください。
変更内容に問題ないか、チェックします。Syntax OKと表示されれば問題ありません。
また、Apacheを再起動させます。

[root@ ~]# service httpd configtest
Syntax OK
[root@ ~]# service httpd restart

以上で、DATE関数のエラーが発生しなくなります。

まとめ

簡単な解決方法ですが、参考になれば幸いです。

Apacheのエラーログについて

Apacheにはアクセスログ(access_log)とエラーログ(error_log)というログを取得しているファイルがあります。
アクセス(access_log)は、Apacheサーバーで提供している情報に外部からアクセスがある都度、ログを取得しています。また、エラーログ(error_log)は、設定によるエラーやアクセスされたファイルが見つからないなどのエラーが発生した場合にそのログを取得しています。
今回は、当サイトで発生したApacheのエラーを参考に、エラーログを説明していきます。

①File does not existのエラーについて

存在しないファイルへのアクセスを要求された場合に記録されるエラーです。
例えば、サイトのページが削除されていたり、ページ内の画像がリンク切れを起こしている場合などに発生します。
いくつかのFile does not existのエラー例を見てみましょう。

[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/example.com/image.gif, referer: http://example.com/index.html

上記エラーは、index.htmlのページで使用されているimage.gifという画像が存在しないというエラーです。
このエラーが発生している場合には、ページに設定してある画像の名前やリンクが正しいか、または画像自体が存在しているか確認してください。

[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/example.com/sample.html

上記エラーは、sample.htmlというページにアクセスしようとしたが、そのページが存在しなかったというエラーです。現在使用しているページでエラーが発生している場合には、ページが削除されていないかなど確認する必要があります。
また、過去に使用していたページで発生してる場合には、過去に使用していたファイル名で「このページは移動しました」といった表示設定することで、ユーザーを新しいページへ誘導することができます。

[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/example.com/favicon.ico

上記エラーは、IEやFirefoxなどのブラウザでアクセスするとアイコンファイルを自動的に読み込もうとするのですが、その際アイコンファイルが見つからなかった場合に発生するエラーです。
アイコンファイルを設定していない場合には、全く気にする必要がないエラーですが、エラーログを不要に記録したくない場合には、アイコンファイルを設置するか、またはhttpd.confでicoファイルに関するエラーを記録しない設定をします。

[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/example.com/robots.txt

上記エラーは、検索エンジンのロボットがクロールした際にrobots.txtファイルを見つけられなかった場合にエラーとして記録されます。
このエラーも気にする必要はありませんが、回避する場合にはrobots.txtファイルを設置してください。

[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/.ssh
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/.ssh
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/.ssh
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/.ssh
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/.ssh
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/passwd
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/passwd
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/password
[日付] [error] [client 接続元IPアドレス] File does not exist: /var/www/html/passwords.txt

このように連続して複数回エラーが発生している場合、攻撃者が不正アクセスを試みようとしていると考えられます。
しかし、ファイルが見つからず不正アクセスが失敗したというエラーですので、エラーログとしては正常なものとなります。

②client denied by server configurationのエラーについて

これはアクセス制限がかかっているエリアへのアクセス失敗や、IPアドレスなどによるアクセス制限によりアクセスが失敗した場合に記録されます。
File does not existエラーはファイルが見つからない場合に発生しますが、このエラーはファイルやエリアへのアクセスを拒否した場合に発生します。

[日付] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/example/

上記エラーは、/var/www/example/にアクセスしようとしたが、アクセス制限などによりアクセス失敗したエラー例です。
/var/www/example/へのアクセス制限を設定している場合には、アクセス制限が成功しているという正常なエラーログとなりますが、設定ミスによるアクセス失敗でも同様のログが残ります。
アクセス制限を設定していないディレクトリやページでこのエラーが発生する場合は、httpd.conf内のorder allow,denyなどの設定が不足しているか、間違っている可能性がありますので確認してください。

[日付] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/html/, referer: http://知らないドメイン

知らないドメインのDNSをアクセス制御する」のような設定を行った場合には、上記のエラーが記録されました。

[Mon Jul 01 05:32:43 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:43 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:43 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:43 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:43 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:45 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:45 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/
[Mon Jul 01 05:32:45 2014] [error] [client 接続元IPアドレス] client denied by server configuration: /var/www/httpdocs/

上記のように短時間に何度もアクセスを試みようとしている場合、DOS攻撃やDDOS攻撃と考えられますので注意が必要です。

③Fatal error(フェイタルエラー)について

このFatal errorは、PHPで処理を停止する程の致命的なエラーが発生した場合に記録されます。
例えば、存在しない関数を呼び出したり、ユーザー定義関数を重複して定義したりすることで発生し、エラーが発生した時点でプログラムの処理が停止していることになります。
PHPで発生するエラーには軽度から重度のものまでレベル分けされており、処理を停止する程のFatal errorは重度のエラーになりますので、早急に改善する必要があります。

[Tue Oct 10 15:24:54 2018] [error] [client 接続元IPアドレス] PHP Fatal error:  Call to undefined function get_header() in /var/www/example.com/blog/wp-content/themes/example/index.php on line 1
[Tue Oct 10 15:24:55 2018] [error] [client 接続元IPアドレス] PHP Fatal error:  Call to undefined function get_header() in /var/www/example.com/blog/wp-content/themes/example/index.php on line 1

このFatal errorを改善するためのヒントは、エラーログに残されています。
上記の例では、…/themes/exampleディレクトリにあるindex.phpの1行目に書かれているget_header()という定義において、Call to undefined function(定義されていない関数が呼び出された)というエラーが発生していることが分かります。
このヒントから、関数名をもう一度確認してみたり、スペルミスがないか確認することなどで改善することがあります。

但し、このエラーログは、あくまでヒントであってエラーの根本原因を得られるとは限りません。そのため、記録された行番号は参考程度にして、その行番号周辺からエラーとなっている原因を探すことをお勧めします。

まとめ

エラーログは、サイトの状況や問題点を報告してくれる重要な情報です。
その情報を基にVPSサーバーの設定やサイトの構成を改善することで、ユーザーのアクセシビリティを向上させることにも繋がります。
VPSサーバーを運営する上では、ログを監視することは重要は作業ですので、ぜひ活用してください。

Webサイトの表示速度を改善する

遅い。当サイトは、WordPressで制作されていますが、Webサイトの表示速度が遅すぎる。
あまりに気になるので、Webサイトの表示速度を改善してみようと思います。

①表示速度を計測する

GTmetrixは、URLを入力すると簡単にWebサイトの表示速度を計測できます。
では、どれくらいの速度がでているのか、現在のWebサイトの表示速度を計測してみます。

Webサイト評価

「Page Speed Grade」はGoogle、「YSlow Grade」はYahooのWebサイト表示評価ですが、それぞれC評価という結果。これは、すぐに改善する必要がありますね。

②問題点の把握

評価結果の下に評価を下げている原因が表示されます。
当サイトの表示速度が遅くなっている原因を見てみましょう。

サイト評価原因

B〜Fまでの評価がいくつかあります。レンタルサーバーなどの環境においては、改善できない項目もあるのですが、当サイトはさくらのVPSサーバーでできる範囲での対策を行います。
簡単にできる改善方法から試してみます。

③.htaccessによるキャッシュの設定

Leverage browser cachingという項目を改善します。現在はF評価の35点、ひどい。
この項目は、ユーザーのブラウザにCSSや画像などの情報をキャッシュ(記憶)させて、ページの読み込み速度を改善しましょうということ。
改善方法は、.htaccessにキャッシュの設定をします。
Webサイトに設置されている.htaccessに下記3行のコードを追加してください。
※.htaccessが有効になっていない場合は、.htaccessを有効にするを参考にしてください。

<Files ~ ".(gif|jpe?g|png|ico|js|css)$">
Header set Cache-Control "max-age=604800"
</Files>

1行目は、キャッシュさせるファイルの拡張子を指定しています。
(gif|jpe?g|png|ico|js|css)なので、gif,jpg,jpeg,png,ico,js,cssの拡張子がつくファイルを指定。
※jpe?gは、jpgとjpegの両方を指定。
キャッシュさせたい拡張子を追加する場合には、()内に追記します。
例として、フラッシュのファイル(.swf)をキャッシュさせたいのなら、

<Files ~ ".(gif|jpe?g|png|ico|js|css|swf)$">
Header set Cache-Control "max-age=604800"
</Files>

と()内末尾にswfと追記するだけ。

2行目は、キャッシュさせる期間を設定しています。
Googleの推奨キャッシュ期間は、1週間から1年間先ということですが、さすがに1年間は長すぎるので、今回は1週間に設定をしています。
※604800は1週間の秒数を表します。キャッシュ期間については、1週間〜1年間で設定してください。
1週間=604800
1ヶ月間=2592000
1年間=31536000

さて、どれくらい改善されたかGTmetrixで再計測。

Leverage browser caching

なんと、F評価(35点)→C評価(76点)と大幅に改善されました!
.htaccessにキャッシュ設定を行うだけで、十分な効果があるようです。

しかし、C評価で留まってしまった理由は、FacebookやTwitterなどの外部ファイルを読み込んでいるのですが、それらの外部ファイルは今回のキャッシュ設定は適用範囲外となっているためです。
なので、htaccessによるキャッシュの設定は完了とします。

④圧縮を有効にする

次は、Enable gzip compressionという項目を改善してみます。現在は、C評価の73点。
この項目は、WebサイトのコンテンツをVPSサーバー側で圧縮し閲覧者のブラウザに送信することで、転送量を減らし高速化しましょうということ。
ただし、VPSサーバー側のCPU使用率が上がるため、状況によっては表示速度が落ちる可能性があります。

この設定を有効にするには、httpd.confまたは、htaccessのどちらかに設定をすることで実現できるのですが、今回はhttpd.confで設定を行ってみます。

httpd.confを開いてに下記のコードがあることを確認してください。ない場合には、httpd.conf内に追記。
このmod_deflateというのが、圧縮をしてくれるモジュール。

LoadModule deflate_module modules/mod_deflate.so

次に、http.conf内の末尾に下記のコードを追加します。

# 転送するコンテンツに圧縮を適用
SetOutputFilter DEFLATE

# 圧縮非対応の古いブラウザへの対応
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

# gif,jpg,pngなど画像コンテンツは圧縮しない
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|ico)$ no-gzip dont-vary

# MIMEタイプ指定で指定したファイルのみ圧縮
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
# AddOutputFilterByType DEFLATE application/xhtml+xml
# AddOutputFilterByType DEFLATE application/xml
# AddOutputFilterByType DEFLATE application/rss+xml
# AddOutputFilterByType DEFLATE application/atom_xml
# AddOutputFilterByType DEFLATE application/x-javascript
# AddOutputFilterByType DEFLATE application/x-httpd-php

上記の設定では、plain,html,xml,cssファイルを圧縮するよう指定しています。xhtmlなどを圧縮指定する場合には、行頭の#(コメントアウト)を削除して有効化してください。

また、特定のディレクトリに適用させるには、下記のように<Directory>や<Location>内に設定します。

<Directory "/var/www/example/">
# 転送するコンテンツに圧縮を適用
SetOutputFilter DEFLATE

# 圧縮非対応の古いブラウザへの対応
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

# gif,jpg,pngなど画像コンテンツは圧縮しない
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|ico)$ no-gzip dont-vary

# MIMEタイプ指定で指定したファイルのみ圧縮
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
# AddOutputFilterByType DEFLATE application/xhtml+xml
# AddOutputFilterByType DEFLATE application/xml
# AddOutputFilterByType DEFLATE application/rss+xml
# AddOutputFilterByType DEFLATE application/atom_xml
# AddOutputFilterByType DEFLATE application/x-javascript
# AddOutputFilterByType DEFLATE application/x-httpd-php
</Directory>

設定が完了したら、httpd.confを保存。
設定を反映させるため、Apacheを再起動します。

[root@ ~]# service httpd restart

それでは、GTmetrixで再計測。

Enable gzip compression

A評価に改善されました!C評価(73点)→A評価(100点)の改善なので、文句なし。
相当な効果があったようです。
Webサイト表示速度の総合評価も確認してみると、
Webサイト評価

「Page Speed Grade」「YSlow Grade」共にB評価でワンランク改善されました。
キャッシュの設定と圧縮の有効化だけで、かなりの効果がありました。
ここまで来たら、A評価を狙いたい!

⑤Keep Aliveを有効にする

最後にEnable Keep-Aliveという項目を改善します。
この項目は、サーバーとクライアント間の接続を開いたままにすることによってサーバーのパフォーマンスを向上させましょうということ。
ただし、Keep Aliveは接続終了の判定が行いにくいため、クライアントが終了したにも関わらず接続が維持されたままにより、逆にパフォーマンスを低下させてしまう恐れもありますので注意。
通常の設定では、Keep AliveをOnに設定しておくものですが、今回はWebサーバー構築時に設定をできていなかったようです・・・。

それでは、httpd.confを開いてKeepAlive Offというコードを見つけてください。

#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive Off

このKeepAliveのOffをOnに編集してください。

KeepAlive On

以上で編集完了ですので、ファイルを保存してください。
設定を反映させるため、Apacheを再起動します。

[root@ ~]# service httpd restart

さて、GTmetrixで再計測をしてみます。

Webサイト評価

「Page Speed Grade」がA評価に改善されました。
3項目の設定で、ある程度の改善が出来ましたので、一度これで良しとします。
「YSlow Grade」のA評価は次回に。