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評価は次回に。

.htaccessでWordPressを守る

VPSサーバーは、ある程度まで構築してしまうと日常であまりやることがありませんw
今回は、WordPressを導入したときの、.htaccessによる基本的なセキュリティ設定方法についてご紹介します。
.htaccessファイルに下記のコードを設定していきます。

①.htaccessの保護

まずは、.htaccessに不正アクセスされては元も子もないので、.htaccessファイル自体をアクセス拒否して保護します。

<Files ~ "^.*\.([Hh][Tt][Aa])">
 order allow,deny
 deny from all
 satisfy all
</Files>

②WordPress内にあるディレクトリ一覧の非表示

WordPress内にあるディレクトリ一覧をブラウザなどで閲覧できないようにします。
当サイトのWebサーバーの構築で既に設定済みですが、念のため。

Options -Indexes

③wp-config.phpの保護

wp-config.phpには、データベース関連のパスワードなど大切な設定が書かれていますので、必ず保護しましょう。

# protect wp-config.php
<files wp-config.php>
order allow,deny
deny from all
</files>

④wp-mail.phpとinstall.phpを保護

wp-config.phpを保護しましたが、さらにwp-mail.phpとinstall.phpもアクセス拒否して保護します。
下記の設定では、wp-config.phpも保護する設定です。

<FilesMatch "^(wp-config\.php|wp-mail\.php|install\.php|\.ht)">
order allow,deny
deny from all
</FilesMatch>

⑤スクリプトインジェクション対策

フォームから不正なscriptタグを入力しても実行処理しないようにします。

Options +FollowSymLinks
RewriteEngine On
# <script> タグ
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
# PHP グローバルに関連するもの
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
# _REQUEST を変更しようとするもの
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
#マッチしたURLへのアクセスを禁止("403 Forbidden"のレスポンスを返す)
RewriteRule ^(.*)$ index.php [F,L]

⑥日本国外からのログイン画面へのアクセスを制限

不正アクセスの多くが日本国外からのため、国内のIPアドレスの場合だけログイン画面にアクセスできるようにし、日本国外のIPアドレスはログイン画面にアクセス出来ないようにします。
また、XML-RPCの脆弱性を狙った不正アクセスの試みも頻繁に発生するため、xmlrpc.phpへのアクセス制限も一緒にします。
下記サイトにアクセスして、「.htaccessダウンロード」ボタンを押して、.htaccessファイルをダウンロードします。その.htaccessファイルに記載されている「allow from IPアドレス」全てをコピーして、下記のハイライトした部分にペーストしてください。
IPアドレスで日本国外(海外/外国)からのアクセスを制限する.htaccess CGI’s

<Files ~ "^(wp-login\.php|xmlrpc\.php)$">
Order deny,allow
Deny from all
allow from 1.0.16.0/20
allow from 1.0.64.0/18
allow from 1.1.64.0/18
allow from 1.5.0.0/16
allow from 1.21.0.0/16
allow from 1.33.0.0/16
(以下、日本国内IPアドレス続く)
...
...
...
</Files>

以上の.htaccess設定でWordPressの基本的なセキュリティは確保できると思います。
上記以外にも.htaccessによるセキュリティ向上方法があった場合には、随時追記していきます。

【参考サイト】
.htaccessをWordPressで徹底活用 | CSSPRO
WP インストール時の注意点とセキュリティ | Web Design Leaves