A からはじめる Apache

Apache とは何だろう?

Web 分野にそれほど関心のない人でも 書店の書棚でそれらしき文面をご覧になったことがあるのではないだろうか Web に関心のある人であれば, なおさらである. おそらく Apache という名前にはこころあたりがあるだろう.

Apache というのは, いわゆる Web サーバの一種であり, Web サーバとしては大変メジャーなソフトウェアである. 読者のみなさんになじみの深い Web サイトが 実は Apache で構築されているということだって めずらしくないはずだ.

ここでは, 冒頭の「Apache と何だろう?」から始めて, Apache の導入・設定について解説を行う. その後に Apache の運用や活用についてのヒントを提示し, 最新に次世代の Apache についての展望について触れる. また, Apache 以外の Web サーバについても, 若干ではあるが, 触れておいた.

Part 1: Apache 概論

Apache というのは, いわゆる Web サーバの一種である. みなさんも Netscape Navigator, MSIE, w3m, lynx, … といった Web ブラウザを使って Web ページをご覧になられるかと思うが, このとき, Web ブラウザに対して情報(すなわち Web ページそのもの)を 送ってよこしてくれているのは, この Web サーバなのである. Web サイトを構築するためには Web サーバが必須であり, それゆえ, Web サーバとよばれる種類のソフトウェアは 非常に多くの場面で利用されている. そんな Web サーバソフトウェアのなかでも Apache はたいへんメジャーな存在である.

ここではまず, そもそも Web というのはどのような仕組みで なりたっているのかということについて考えてみようと思う.

Web の仕組み

こと Web をめぐる世界では一つの用語が さまざまな意味で用いられることがめずらしくない. 加えて新しい用語がどんどん出現する世界でもあるので 慎重に用語を選択しなてはならない場面がなくはない.

そこで話をはじめる前に, いくつかの用語についての 本稿のこれ以降の部分における意味を明らかにしておこうと思う.

Web

すでにおなじみとなった URL によって表すことのできる すべての情報を含めた, いわば総合メディア. http:// ではじまる, 俗にいう「ホームページ」をはじめとし, FTP サイト(ftp:// ではじまる)や ネットニュース(news: ではじまる)などといった いくつかのメディアをその中に内包している.

また, ややあらっぽい言い方になるが, ブラウザ(の「URL を開く」など)によって参照できるすべての情報を イメージしてもらっても, そうひどくかけはなれてはいないだろう.

HTTP

http:// で始まる URL によって参照できる情報を 転送する際の取り決め(プロトコル). たとえば <URL:http://www.apache.org/> によって参照できる情報は, お使いのブラウザと www.apache.org なるサーバが この HTTP というルールに従ったやり取りを行ったすえに みなさんのお手元に届けられたものである.

HTTP サーバ

「Web」に含まれるメディアにおいて, HTTP によって参照できるメディアは Web の代表格と言ってもさしつかえないだろう. その HTTP を司るのが HTTP サーバである. したがって上の HTTP についての説明で 「www.apache.org なるサーバ」と言ったのは, 正しくは「www.apache.org なるホストで動作している HTTP サーバ」と表現すべきである.

HTTP サーバの詳細については後述するが, Web のすべてをカバーするような サーバソフトウェアは存在していないか, 少なくとも一般には利用されていない. そのため, 通常は HTTP や FTP などのそれぞれに対応した サーバソフトウェアを複数利用して, 全体として Web サイトを構成している(図 1).

ブラウザ

最後にブラウザであるが, これはみなさんの方が よくご存知だろう.

ブラウザは万能の情報ビューアーで, 一般にはそれ一つだけで複数のメディアに対応できる. したがって実質的に「Web サーバ」は存在し得ないと考えられるのに対して 「Web ブラウザ」はすでに存在しているわけである.

さて, このような定義にのっとるとすると Apache は HTTP サーバであるということになる. では HTTP サーバとは――ひいては Apache は どのような働きをするのかについて考えて行くことにしよう.

HTTP サーバの働き

前述した通り, http:// によって参照できる情報は HTTP サーバによってもたらされている.

たとえば次のような URL によって表される情報を ブラウザによって参照する際に何が起っているのかをみてみよう.

http://www.apache.org/httpd.html

一つの URL の中には数種類の情報がつまっている. まず「http」という部分だが, これはその情報のメディアを意味している. これはつまり, その情報にアクセスするための方法を表しているとも言えるだろう. 続く「www.apache.org」はその情報を保持しているホスト名であり, 最後の「httpd.html」は個々の情報を特定するためのパスである. 総合すると「www.apache.org で http というメディアとして 提供されている情報郡の中の httpd.html という情報」というわけだ. 少々話がそれるが, 仮にこれとよく似た ftp://www.apache.org/httpd.html によって参照できる情報があったとしても, メディアが違っているためそれによって取り出せるのは 一般にまったく別の情報となる *1.

ブラウザの動作

さて http://www.apache.org/httpd.html として 情報の所在地を指定されたブラウザは, まずホスト www.apache.org の 80 番ポートに接続する. ポートというのはお役所の窓口のようなもので, 窓口の奥には様々なサービスを提供すべく係員がひかえている. なぜ 80 番につなぐのかというと, 一般に HTTP サーバがひかえているのが 80 番窓口だからである. この関係は決りきっているもので, このようなポートのことを well known port と呼ぶこともある. 同様に係員の関係が決りきっているような窓口(つまり 80 番以外の well known port)には他にもあって, たとえば 21 番窓口では FTP サーバが待ちうけているし 119 番窓口ではニュースサーバが待ちうけている.

ところで HTTP サーバは 80 番窓口にしか配置できないのか? というと必ずしもそういうわけではない. 各種ソフトウェアの設定によっては 80 番以外にも HTTP サーバを配置することができる. しかし, そのようにして通常以外の窓口に配置された場合, ブラウザはその HTTP サーバの存在を知ることはできないため, そのような特別な窓口から情報を受け取りたいときには 明示的にポート番号を指定してやらなくてはならない. すなわち http://www.apache.org:8080/ のようにである. このようにするとブラウザは www.apache.org の 80 番ポートの代わりに 8080 番ポート*2に接続しようとするのである.

ともあれ www.apache.org のしかるべきポートに無事接続できると, ブラウザは続けて HTTP によるやり取りを開始する. HTTP は比較的簡単なプロトコルである. 基本的にはブラウザからリクエストを送信し, HTTP サーバがレスポンスを返すというやり取りを 必要なだけくり返すだけだ(図 2). わざわざ必要なだけくり返すと言ったのは, 一つのリクエストによって得ることができるのが 一つの情報だけだからである. 例として http://www.linux.or.jp/ について詳細に見てみよう. 図 3 はこの原稿を執筆した時点での http://www.linux.or.jp/ を Netscape Navigator で 参照している様子である.

図 3: http://www.linux.or.jp/ のイメージ

ご覧の通り, このページにはページの本文以外に 2 つの画像イメージが貼り込まれている. 「目次」の上と「おしらせ / 新着情報」の右側にいる 大小のペンギンがそれである. このようなページを作ったことがある方はご存知だろうが, このようなケースでは本文部分, 小ペンギンのイメージ, 大ペンギンのイメージと都合 3 つの部品(具体的には ファイルだが)によって一つのページが構成されることになる. HTTP においてはこのような一度に一つの部品しか取得できないので, 結局ブラウザは次のように動くことになる*3.

  1. www.linux.or.jp の 80 番ポートに接続.
  2. 「トップページがほしい」とリクエストを送り出す.
  3. それによってトップページが得られると, それを解析して追加リクエストを出さなくてはならないような 部品がないかどうかを検証する.
  4. もしもそうした部品が必要だったら, さらに「小ペンギンのイメージがほしい」といった リクエストを送り出す.
  5. 必要なだけリクエストを送り, そしてレスポンスを得る.

もちろんこの後に(あるいは途中で)部品を画面上にうまくならべる という作業が入ってくるし, 実際には効率を良くするために もう少し複雑なやり取りをしているのだが, 簡単のためにここでは扱わないことにする.

では, 続いて HTTP サーバの動作を見て行きたい.

HTTP サーバの動作

前述した通り, HTTP サーバは 80 ポートか, あるいは特に指定されたポートにて クライアント(多くはブラウザだが)からの接続と それに続くリクエストを待ち受けている. クライアントからのリクエストには 通常次のような情報が含まれている.

ここにあげたものよりも多くの情報を含んでいる場合も 少ない情報しか含んでいない場合もあるが, サーバはこうしたリクエストを受けると ただちに要求された情報を送り返すための作業を開始する. それは, 大ざっぱにいうとこのような内容であろう.

まず一つには要求されたパスに対応する ホスト中のファイル(データ)を特定しなくてはならない. 多くの場合, HTTP サーバの設定によって 起点となるディレクトリが指定してあって, そのディレクトリの位置にリクエスト中の / を マップさせるといった形になる(図 4). そのようにした結果, 該当するファイルが見つかれば そのファイルの内容をレスポンスに入れて送り返すべきデータであるとする. ただし, この時, サーバの設定によっては 特定されたファイルが CGI プログラムである と判断される場合がある. その場合, 単にファイルの中味を送り返すのではなく, ある特定の情報を与えた上でそのファイルを実行し, その出力をレスポンスとして返すべきデータとする. このことについては Part 2 で 詳しく解説しているのでそちらも参照してほしい.

ともあれレスポンスとして送り返すべきデータを特定できたら, 次にはそのデータについての属性情報を用意しなくてはならない. というのは, ブラウザはサーバからのレスポンスに付属している そうした属性情報によって得られたデータ(部品)の 種類などを判定しているからである. 主な属性情報としては そのデータがテキストなのかイメージなのかといったことや, データの鮮度を示すための最終更新日付けおよび, データの有効期限を示す日付け. またそのデータがテキストであるなら どのような形式のテキストであるか(HTML とか 形式が特に定まらない plain テキストとか)や どのような言語(英語とか日本語とか)で書かれているか といったものなどが考えられる.

こうした HTTP サーバとしての基本的な仕事の他にも アクセスログへの記録やアクセス制限についてのチェックなどを 一般には行っている.

コラム: HTTP の詳細

すでにご存知の方も多いかと思うが, telnet コマンドによって任意ポートに 接続することが可能である.

それがどういうことかというと, こういうことだ. 一般的な HTTP サーバが 80 番ポートで 接続を待ち受けているということを述べたが, そのポートに telnet コマンドによって接続する ――つまり, telnet コマンドによって HTTP サーバとやり取りをすることが可能だということになる. HTTP サーバだけではない. メール配送を行う SMTP サーバや ネットニュースを扱う NNTP サーバなど, さまざまなサーバとやり取りをすることが可能だ.

もちろん各種のサーバとやり取りをするためには, それに応じたルール(プロトコル)を知っている必要があるのである. しかし, 先に挙げたサーバについては 簡単なテスト程度なら比較的単純なやり取りで済むことが多い.

HTTP の例を見てみよう.

$ telnet localhost 80
T: Trying 127.0.0.1...
T: Connected to localhost.
T: Escape character is '^]'.
I: GET / HTTP/1.0
I: Host: localhost
I: 
O: HTTP/1.1 200 OK
O: Date: Mon, 11 Sep 2000 21:33:36 GMT
O: Server: Apache/1.3.12 (Unix) Debian/GNU mod_ruby/0.1.9 Ruby/1.6.0(2000-09-01)
O: Content-Location: index.html.ja
O: Vary: negotiate
O: TCN: choice
O: Last-Modified: Fri, 08 Sep 2000 12:29:43 GMT
O: ETag: "e6bac-358-39b8dbb7;39bd4f69"
O: Accept-Ranges: bytes
O: Content-Length: 856
O: Connection: close
O: Content-Type: text/html; charset=iso-2022-jp
O: Content-Language: ja
O: 
O: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
O:   "http://www.w3.org/TR/REC-html40/loose.dtd">
O: <HTML>
      :
    (略)
      :
O: </HTML>
T: Connection closed by foreign host.

筆者が入力したのは I: の部分であり, それに対する HTTP サーバの応答が O: の部分だ. T:telnet コマンドの出力なので無視してかまわないが, 最後の部分で接続が切られたことがわかる.

HTTP には 2 つのバージョンがあるが, そのうちの古い方が 1.0 である(新しい方は 1.1). ここで示したやり取りはバージョン 1.0 に基づくものだ. HTTP 1.0 では接続→リクエストを送出→ レスポンスが返ってくる→接続を切られるというサイクルで すべてが行われている. この例でリクエストにあたるのは I: の部分であり, レスポンスにあたるのは O: の部分である.

この例では少しわかりにくいが, リクエストとレスポンスは同じような形式になっている. つまり, リクエストのコマンドやレスポンスの種類を示す 一行が最初にあり, その後任意の数のパラメータが ちょうどメールのヘッダのような形でならんでいる. さらに一行の空行があってデータが続いているという形式だ.

この例のリクエストにはデータ部分がないが, これはリクエストコマンドが GET というものであるためである. GET コマンドはページを取りよせるためのコマンドなので, サーバにデータを送出しない. これに対して POST というサーバにデータを送り付けるための コマンドもある. POST は, たとえアンケートページなどのような FORM タグを使ったページで送信ボタンを押したときなどに 使われるコマンドだ(どのコマンドを使うかはブラウザが 判断してくれるいる).

リクエストコマンドの行は 説明するまでもなく意味を想像できるだろう. まずコマンド名があり, そのパラメータとして この場合取りよせたい URL のパスの部分がきている. 最後にあるのは使用する HTTP のバージョンである. 続く Host: にはアクセスしているホスト名を指定する. これは一見無駄に思えるかもしれないが, バーチャルホストを運営する場合には この情報が非常に重要となってくる.

次にレスポンス部分の説明をしてみよう. 最初の一行はリクエストされた情報についての状態を示している. まん中の 200 という 3 桁の数値はステータスコードとよばれ, リクエストが成功したか失敗したか, あるいはその他の状態なのかといった意味を表している. もちろん成功というのはリクエストした情報を 得ることができるようなケースである. では失敗というのはどういうことかと言うと, そもそもその情報が存在していないとか アクセス制限によってリクエストそのものを拒絶した場合などだ. それぞれ 2xx あるいは 4xx といったステータスコードになる.

ステータスコードの前はサーバが使用する HTTP のバージョンであり, ステータスコードの後はそのステータスについてのコメントである. HTTP 1.0 のリクエストに対して HTTP 1.1 で返してきているのが 興味深いところである.

それらに続くのはレスポンスヘッダと呼ばれる付加情報で, 情報そのものについての補助的な情報であることもあるし, そうでないこともある. たとえば Last-Modified というのは その情報の最終更新日付けを表しており, Content-Type はその情報のタイプ(text/html, HTML 形式のテキスト)と 場合によっては文字コード情報(charset=iso-2022-jp, 文字コードは ISO-2022-JP)を表しているといった具合だ.

レスポンスヘッダと 情報そのものは空行で区切られる. この例では, 空行から下はリクエストに対応する ファイルの中味になっている.

どうだろう. HTTP でのやり取りの雰囲気は伝わっただろうか.

最後に実際のブラウザがどのようなリクエストを発するのかを 参考までに見ておこう. ここではその目的のために nc(netcat)というコマンドを利用した.

$ nc -l -p 8888
I: GET / HTTP/1.0
I: User-Agent: w3m/0.1.10
I: Accept: text/*, image/*, audio/*, application/*
I: Accept-Language: ja,en
I: Host: localhost:8888
I: 

nc はその名の通りネットワーク対応の cat コマンドである. -p オプションでポート番号を指定し, さらに -l オプションを付けて実行すると そのポートで接続待ち受け状態になる. 上のサンプルはその状態で, ブラウザ w3m によってそのポート 8888 にアクセスしてみた ときの様子である.

ちなみに -l オプションなしで nc を起動すると クライアントとして任意のホストの任意のポートに 接続することができる. 最初の telnet の例を nc で行うとすると このような感じになるだろう.

(echo GET / HTTP/1.0; echo Host: localhost; echo) | nc localhost 80

このように nc は便利なコマンドなので 興味を持たれたらインストールしてみて, 実際に使ってみてほしい.

HTTP サーバ Apache

前節での定義によるなら Apache は HTTP サーバの一種ということになる. HTTP サーバとしての Apache は もっともメジャーなもの中の一つでもあり, ある調査*4によれば その名前が知れ渡っているだけはなはく, 実際にもっとも使われているサーバでもあるようだ.

ではなぜ Apache なのか?

ここで Apache の歴史とその機能について触れておきたい.

Apache の歩み

現在の Apache は Apache Software Foundation(ASF)によって管理されている. その ASF 自身(<URL:http://www.apache.org/ABOUT_APACHE.html>)によれば HTTP サーバとしての Apache には ベースとなった別の HTTP サーバソフトウェアが存在している.

1994 年当時もっともメジャーであった NCSA の HTTP サーバがそれだ. ところがその当時すでに NCSA の HTTP サーバの開発は事実上ストップしてしまっており, 困ったサーバ管理者たちは個々に独自の改良をほどこすことで その場をしのいでいるような状態であった. そうした中, 一つの動きが現れた. 個々の改良点を持ちよって, それをパッチという形で統合しようというのである. Brian Behlendorf *5 と Cliff Skolnick は そのためにメーリングリストと開発用のマシンを用意し, この動きをある程度組織だったものとした.

各々の改良を待ちよって(あるいは集めて)作った Apache サーバが最初に公開されたのは 1995 年 4 月, バージョン 0.6.2 でのことである. Apache のリリースは大変な反響をよび, さらなる開発が続けられた. その結果, 最初のリリースの次のバージョンである バージョン 0.7.x では (今となっては当り前となっているのだが) 事前に Apache サーバの分身を作っておいて トータルのレスポンス良くするという新機能が追加された. しかし NCSA の HTTP サーバをベースとしての改良には 限界があることがすでにわかっていたようで, Apache 開発グループはバージョン 0.7.x の開発を行う一方で 新しい設計に基づくサーバを開発するという 大がかりな作業をしていたのである. 結果的に, それらの成果は Apache サーババージョン 0.8.8 として われわれの目に触れることとなった.

新しく書き直された Apache には いくつかの新しい特長があったが, そのうちの一部は以下のようなものであった.

その後, テストが重ねられ, さらなる新機能が加わり(もちろんモジュールとして), バージョン 1.0 の Apache がリリースされたのは 1995 年 12 月 1 日のことだった.

Apache の開発は現在も継続的に行われており, バージョンも進んでいる. 当初小さなグループでしかなかった Apache 開発グループは 今や多くのユーザにささえられた 大きなグループとなっている.

Apache の機能

こうして開発の続いている Apache の現在を 少々のぞいてみよう.

Apache サーバの FAQ には Apache サーバの特長として 次のような事柄が挙げられている. まずパワフルで柔軟であること. 柔軟であるというのは, 設定によって Apache の挙動を変えられる範囲が広いということと, モジュールによって Apache の機能自体を拡張できるということの 二つの面について言えることであろう. 特に後者については Apache の開発グループ以外から さまさまなモジュールがリリースされていることからもうかがえる. このことには Apache のためのモジュールを作るために必要な API が ある程度整えられていることが強く影響しているはずだ.

次に Apache が動作する環境の多さが取り上げられている. 各種の UNIX 環境はもちろん, MS-Windows NT/9x や Netware 5.x, OS/2 などがその代表だが, これ以外にもサポートしている環境はあるようだ. さまざまな環境, とりわけ OS そのものがまったく違う環境であっても 同じソフトウェア(ほぼ)同じように動作するというのは 場合によっては非常に大きな強みとなる.

また, くり返し実装してほしいと言われてきた 機能の多くを実装してきたことも特長の一つである. たとえば認証のためのデータべースに dbm を使えるようにすることや, リクエストされたページが存在しないときなどの エラーメッセージをカスタマイズできるようにすること. データの形式についてできるだけクライアントの 要求に沿う形でレスポンスを返すための仕組みや, バーチャルホストなど数え上げればきりがない.

そして今なお活発に開発が続けられていることと, ユーザからのフィードバック, 特に不具合についてのレポートや 新しいアイデアなどが奨励されていることも 特長の一つとして数えてもよいだろう.

そして最後にこのことを挙げておきたい. Apache の今日があるのには, 自身がオープンソースソフトウェアのやり方をつらぬいてきた ということが非常に強く影響しているはずだ. Apache はプロジェクトのスタートからして オープンソース的なやり方ではじまっていると言えるだろう. さまざまな観点からの改良がされ続けてきたのも オープンソースであったからこそであろうし, 現在のようなシェアを確立した要因の一つにすら 数えることができるだろう(オープンソースということについては コラム: オープンソースのやり方も参照してほしい).

コラム: オープンソースのやり方

オープンソースのやり方がビジネスに対してどう働くのかは 大変興味深いが, まだ結果は出ていないし, おそらくまだ当分は出てこないだろう. しかしオープンソースであることが ソフトウェア自体に変化をもたらすことはたしかなようで, しかも, その変化のうちの多くが おおよそ良い方向への変化であるようだ. その一方で「オープンソースのやり方」であることと 直接結び付かない部分の問題が 浮き彫りになってきてもいるのではないかと思えることもある.

ソースが公開されているというのはごく基本的なことであるから, このことをなくしてはオープンソースのやり方を 実践することはできないのはたしかである. しかし本当に重要なのは, われわれユーザがそれをどのように扱えるのかという点である. ソースを見られるという状態は 見られないよりははるかにマシだし, それはそれで素晴らしいことではある. が, しかし, もしもそれが「ただ見られるだけ」にすぎないのであれば, そこにはそれほど大きな意味を見出すことができないだろう.

ともすれば, キーワードだけがとり出され, その文脈がないがしろにされかねない時節だけに, 今一度オープンソースの定義*6にたちかえってみるべきではないだろうか.

Part 2: インストール

前置きが長くなってしまったが, ここで実際に Apache のインストールと簡単な動作確認をやってみよう. Apache に限らずソフトウェアのインストールというと, 以前はソースを取りよせて自分でコンパイルし, それをやはり自分でインストールするという手順が必要だったが, 今ではバイナリパッケージが提供されていることもめずらしくはない.

バイナリパッケージについては賛否両論あるようだが, 筆者は, 十分に信頼できる品質のよいバイナリパッケージを使うのであれば 不慣れな管理者が自ら手作業でインストール作業を行うよりも 良い結果になることが多いのではないかとも思っている. ただし, バイナリパッケージは一般に元々のソフトウェアの 新バージョンのリリースよりも遅れてのリリースになりがちであるので, その点には注意が必要である. 特にセキュリティに関係するリリース情報については よくチェックしておきたいし, その方面のケアを きちんと行っているパッケージを選択したい.

以下では Linux 系ディストリビューションのうちの メジャーなものと FreeBSD についての バイナリパッケージからのインストール方法と, 参考までにソースからのインストール方法の概要を紹介する.

Red Hat Linux 7.0

Red Hat Linux 7.0 では Apache の最新バージョンである 1.3.12 をベースとしたバイナリパッケージが提供されている (Red Hat Linux 6.2 でも同様). インストールの際に Server を選択するか, あるいは Custom を選ぶとインストールの完了とともに Apache パッケージが使えるようになる.

Red Hat Linux 7.0 はリリースされたばかりなので, 現時点で Apache パッケージのバージョンアップが 必要ということはおそらくないと思われるが, 今後のためにアップデート情報などについて触れておこう. パッケージのバージョンアップが必要となるのには いくつかの原因が考えられる.

理由は様々であり得るが Red Hat Linux の場合, この種の不具合・アップデート情報は <URL:http://www.redhat.com/apps/support/updates.html> から得ることができる. また, 新しいパッケージをネットワークを使って取りよせるには <URL:http://www.redhat.com/download/mirror.html> にあるミラーサイトの中から適当なサイトにアクセスすると良いだろう. 新しいパッケージについては「Updates」の欄に書かれている ところから入手できるはずである.

なお rpm コマンドを使えば パッケージがすでにインストールされているかや インストールされているパッケージのバージョンを 確認することができる.

$ rpm -q apache
apache-1.3.12-25

あるいはこうだ.

$ rpm -qa | egrep 'apache'
apache-manual-1.3.12-25
apache-devel-1.3.12-25
apache-1.3.12-25

このように apache パッケージについての情報が表示されれば apache パッケージがインストールされているということであり, そのバージョンが 1.3.12-25 であることがわかる.

こうして調べた結果, より新しいバージョンがリリースされてることや, あるいはパッケージがまだインストールされていないことがわかったら, 次のようにして(より新しいバージョンの)パッケージを インストールすることができる. こことで使うのはやはり rpm コマンドだ.

$ su
# rpm -Uvh apache-1.3.12-XX.i386.rpm

オプション -Uvh に続いているのはパッケージファイル名で, この部分には http:// や ftp:// ではじまる URL を 直接指定することもできる. そうした場合には rpm コマンドが指定された場所から パッケージファイルを取りよせた上でインストール作業を行ってくれる.

パッケージをバージョンアップした際の 一般的な注意事項として, コンフィグレーションファイルの問題を 挙げることができるだろう. というのはソフトウェアのバージョンアップによって コンフィグレーションファイルの 書式が変更になることが時々あるからである. こうした場合, rpm はすでにあったファイルに .rpmsave という拡張子を付けて保存した上で 新しいコンフィグレーションファイルをインストールしてくれる. また, その場合には, そうした処理をした旨のメッセージも表示されるので 早目にコンフィグレーションファイルを見なおしておこう.

Vine Linux 2.0

Vine Linux 2.0 はリリースされてから半年ほどたっている. そのため Apache のバージョンは 今となっては若干古めの 1.3.11 をベースとしたパッケージになっている. ただし Vine Linux 2.0 に関してのアップデート情報を扱った <URL:http://vinelinux.org/errata/2xi386/index.html><URL:http://vinelinux.org/getvine.html#ViaFTP> にあるミラーサイトの Vine-2.0/updates/i386 以下を見ればわかるように, バージョン 1.3.12 をベースとした より新しいパッケージがリリースされている.

Apache のこのバージョンでは 1.3.11 以前にあったセキュリティ上の 問題が解決されているため, もしもまだであればこの機会にバージョンアップしておくことを おすすめする.

バージョンアップに際しての注意点は Red Hat Linux 7.0 についてと同じである. またインストール方法やパッケージの確認の方法についても同様だ.

$ rpm -qa | egrep 'apache|httpd'
apache-1.3.12-2vl3
      :
$ su
# rpm -Uvh apache-1.3.12-2vlX.i386.rpm

もちろん http:// や ftp:// を指定してのインストールも可能である. また, Vine Linux 2.0 をインストールした際に Server としてインストールするか, または Custom でインストールすれば インストール直後から Apache が使える状態に なっているはずである.

Debian GNU/Linux 2.2 (potato)

Debian GNU/Linux 2.2 はリリースして間もないのだが, 例によってリリースに非常に時間を要した関係で Apache が古い. 執筆時点でバージョン 1.3.9 をベースとしたパッケージが提供されている. ただし 1.3.12 以前に見つかったセキュリティ上のバグなど 重大な問題についてはバックポートパッチなどによって 解決されているようである*7.

Debian GNU/Linux でのパッケージのインストールには apt-get を使用するのが一般的であろう. まず次のようにしてローカルにあるパッケージ情報を更新する.

$ su
# apt-get update

そうした上で次のようにパッケージ名 apache を 指定して apt-get install を実行する.

# apt-get install apache

これによって必要であれば――つまりすでにインストールされている Apache のパッケージよりも新しいパッケージが ディストリビューションにあれば, それを取りよせて インストールを行ってくれる.

パッケージのバージョンアップに際しての 一般的な注意事項は本質的に Red Hat Linux 7.0 などの場合と同じであるが, 現行の Apache パッケージについては コンフィグレーションファイルを特に扱わない. 反面, コンフィグレーションファイルに 変更があっても気付きにくいので その点での注意が必要だ.

ところで <URL:http://www.debian.org/security/> にある通り apt-get の設定ファイルである /etc/apt/sources.list に 以下の一行(sources.list に書くこのような記述を apt-line と呼ぶことがある)を加えておくことをおすすめする.

deb http://security.debian.org/ potato/updates main contrib non-free

このサイトには特にセキュリティ上必要と思われる アップデートパッケージが置かれることになっている. どのような問題が発見されたかについては <URL:http://www.debian.org/security/> で参照できる. また, セキュリティ以外の問題で アップデートが必要となったパッケージについての情報は <URL:http://www.debian.org/releases/stable/#errata> から得ることができるようになるはずである.

ここで apt-get の使い方をもう一つ.

# apt-get dist-upgrade

こうすると apt-get は, 現在インストールされている すべてのパッケージを最新のものにしようとする. このため sources.list が正しく設定されていれば 定期的に apt-get を実行するだけで パッケージの更新に追従することが可能となる. ただし, それと同時に予期しないパッケージまで バージョンアップされてしまう可能性があることを 注意事項としてあげておく(もちろんリリースされた安定バージョンでは そのことすら問題とはならないはずであるが).

ちなみに現在パッケージがインストールされているかどうかや, インストールされているパッケージのバージョンを調べるには 従来通り dpkg コマンドを使用する.

$ dpkg --list 'apache*'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ 名前           バージョン     説明
+++-==============-==============-============================================
ii  apache         1.3.12-2       Versatile, high-performance HTTP server
     :

結果はご覧の通りである. もしもインストーされていなければこのようなメッセージが 表示されるだろう.

$ dpkg --list 'apache*'
apache* に一致するパッケージが見つかりません。

即座に apt-get を実行しよう!

FreeBSD

(別の方が担当)

手作業でのインストール

最新に簡単にではあるがパッケージにたよらないで Apache をインストールする方法を紹介しておこう.

まず Apache のソースの入手先だが <URL:http://www.apache.org/mirrors/> にミラーサイトの一覧があるので, 適当なサイトを選ぶとよいだろう. サイトが決ったら最新バージョンのソースを取りよせよう. Apache のソースは一見してバージョンがわかるようになっている.

apache_X.Y.Z.tar.gz 

この中の X.Y.Z の部分がバージョンを表していて, 1.3.9 とか 1.3.12 のようなのがそれである. ちなみに 1.3.12 は 1.3.9 よりも新しく 2.0a1 は 1.3.12 よりもさらに新しい.

さて, それでは最新の Apache を取りよせたいわけだが, ここで注意が一つ. 執筆時点では各ミラーサイトに apache_2.0a6.tar.gz というファイルがあり, これが最も新しそうであるのだが, バージョン 2.0a6 の中の「a」はすなわちアルファ版の「a」で, これはつまりテストバージョンであることを意味している. そのため, それがどういうものであるのかを わかっている人だけが使える という種類のものである. どういうことかわからない人は バージョンの中に「a」や「b」が入っていない apache_1.3.12.tar.gz を選択しよう.

ソースを入手できたら, これを展開する. apache_1.3.12.tar.gz は tar でまとめたものを gzip で圧縮したものである. これを展開するためには同じく tar と gzip が必要となる. 具体的にはこのようにする.

$ gzip -dc apache_1.3.12.tar.gz | tar xvf -

するとカレントディレクトリに apache_1.3.12 というディレクトリができて その中にソースが展開されるはずだ. もしも途中で以下のようなメッセージが表示されたら, せっかく取りよせたソースだが不完全な状態であるので もう一度取り直す必要がある.

gzip: stdin: unexpected end of file

無事ソースを展開できたら, 新しくできたディレクトリ apache_1.3.12 に移動しみよう.

$ cd apache_1.3.12
$ ls
ABOUT_APACHE  LICENSE        README.configure  config.layout  logs/
Announcement  Makefile.tmpl  WARNING-NT.TXT    configure*     src/
INSTALL       README         cgi-bin/          htdocs/
KEYS          README.NT      conf/             icons/

このディレクトリにある README, INSTALL, README.configure といったファイルに インストールに際してのヒントがある. これらをよく読めば多くの環境(特に Linux ベースの OS や FreeBSD など)ではうまくインストールできるだろう. ただし Apache のソース以外に以下のソフトウェアが 必要となるので注意が必要だ.

では, ここで, ごく基本的なインストール手順を示しておこう. INSTALL ファイルに書かれている通り, とりあえずインストールするには次のようにすればよい.

$ ./configure --prefix=PREFIX
$ make
$ su
# make install

最初にすべきことは configure スクリプトを実行することである. configure スクリプトはみなさんがお使いの OS 環境を 自動的に判別して, それに合うようなコンパイル作業を 行えるように準備をするためのものである. また, 適宜オプションを指定してやることで コンパイル時のパラメータを変更することができる. たとえば Apache のモジュールについての調整をしたければ そのためのオプションを configure スクリプトに 与えておかなくてはならない.

上の例の --prefix もそうしたオプションの中の一つで, 「PREFIX」の部分に Apache をインストールする起点となる ディレクトリを指定することができる. 特に --prefix を指定しなければ /usr/local/apache 以下にインストールされるが, たとえば /opt を指定すれば /opt 以下にインストールされることとなる. 実際にどういうところにインストールされるのかを 事前に知りたければ, 次のように configure スクリプトを 実行するととよいだろう.

$ ./configure --prefix=/opt --show-layout

ただし, これのように実行したときには 前述したような準備はしてくれないので, その後で改めて configure スクリプトを実行しなくてはならない.

configure スクリプトに続く make コマンドの実行によって, 実際のコンパイル作業が行われ, さらに続く make install によって 適切な場所へのインストールが行われる. インストールの際には通常 root 権限が必要となる.

以上で各種の環境に応じたインストール方法の解説を終え, いよいよ次節では Apache を動かしてみようと思う.

動作確認

ここまでに示した中のいずれかの方法によって Apache をインストールできたら, 次は実際に Apache を動かしてみたい. ただし, まだここではテスト的に ともかくそれなりに動かすというだけにしておこう. 本格的な運用を始めるために必要となる設定 (おそらくバーチャルサーバの設定や CGI のための設定などが必要となるだろう)については 続く Part 3: 覚えておきたいApacheの運用法 および Part 4: 目的別カスタマイズ にて詳しく解説したいと思う.

さて, 実際に動かすために必要な作業はなにかというと, 実はなにも必要ではない. もちろん起動するというそれ自体は必要であるが, 各ディストリビューションのパッケージを利用した場合でも 手作業でインストールした場合でも, 正しくインストール作業が完了していさえすれば その状態で起動くらいはきるようになっているはずである.

以下で各々の場合について見て行こう.

Red Hat Linux 7.0 と Debian GNU/Linux 2.2 の場合

多くの Linux 系ディストリビューションでは SysV スタイルの起動スクリプトを採用している. Red Hat Linux もその一つに数えられるが, 今度リリースされた 7.0 では FHS に従うようになった関係で 起動スクリプトの位置が変更された.

Red Hat Linux 6.2 までは /etc/rc.d/init.d 配下にあったものが 7.0 からは /etc/init.d 配下に移動し Debian GNU/Linux と同じになったのである. そういうわけで Red Hat Linux 7.0 と Debian GNU/Linux では Apache のパッケージをインストールすると同時に /etc/init.d 以下に起動スクリプトも インストールされるているはずだ. ここでの起動テストもこれを使って行う.

まず Red Hat Linux 7.0 の場合だが, root にて次のように起動スクリプトを実行してみしう.

# /etc/init.d/httpd start
Starting httpd: [  OK  ]

上の実行例のように OK と出るはずである. ps コマンドを使って Apache サーバが 動いていることを確認してみよう.

$ ps ax | grep httpd

プロセスの存在を確認できたら, 適当なブラウザで そのホストに対してアクセスしてみてほしい. もちろんネットワークの設定が完了していなくてはならないが, それさえできていれば他のマシンからでもそのホストで動き出した Apache サーバにアクセスできることを確認できるだろう.

なお, 今回は手動で起動スクリプトを実行したが, 次回にマシンを再起動した時には自動的に Apache サーバが起動されるようになっている. もしもこれを止めたければ chkconfig コマンドを次のように使うとよい.

# chkconfig --del httpd

再び自動的に起動させたくなったらこうだ.

# chkconfig --add httpd

続いて Debian GNU/Linux 2.2 の場合について説明しよう. Debian でも起動スクリプトは同じ位置にあるのだが ファイル名が異っていて /etc/init.d/apache となっている. 従って Apache サーバの起動はこのように行う.

# /etc/init.d/apache start
Starting web server: apache.
/usr/sbin/apachectl start: httpd started

ちなみに停止はこうだ.

# /etc/init.d/apache stop
Stopping web server: apache.
/usr/sbin/apachectl stop: httpd stopped

次のようにすればコンフィグレーションファイルの 再読み込みをさせることもできる.

# /etc/init.d/apache reload
Reloading apache configuration.
/usr/sbin/apachectl graceful: httpd gracefully restarted

先ほどは特に触れなかったが, Red Hat Linux についても同様の方法で 停止および再読み込をさせることができるので, 起動テストが終ったらひとまず Apache サーバを停止させておこう. 逆に Debian において chkconfig に相当するにはどうするかだが, これには update-rc.d というコマンドを使用する.

# update-rc.d apache defaults

これでマシン起動時に Apache サーバも起動するようになる.

# update-rc.d -f apache remove

このようにすれば起動しないようにできる. コマンド名は違うが, 基本的には同じことをしたことになる. 詳しくは chkconfig や update-rc.d のマニュアルページも 参照してほしい.

Vine Linux 2.0(および Red Hat Linux 6.2)の場合

今度は Vine Linux 2.0(と Red Hat Linux 6.2)についてだが, 起動スクリプトの場所が異っているだけで 基本的には Red Hat Linux 7.0 と同様だ.

となると起動スクリプトがどこにあるかということになるが, すでにおなじみの通り, 起動スクリプトは /etc/rc.d/init.d/httpd にある. あとは Red Hat Linux 7.0 に準ずるわけだから 以下のように実行すればよい.

# /etc/rc.d/init.d/httpd start

なお chkconfig についても同様である.

FreeBSD の場合

(別の方が担当)

手作業でインストールを行った場合

手作業でインストールした場合には Apache に同梱の起動スクリプトである apachectl を使うと便利だろう. apachectl は configure --show-layout で sbindir として表示されるディレクトリに インストールされているはずである.

使い方は Red Hat Linux などの 起動スクリプトに似ていて, オプションに start, stop などを指定できる. ただし再読み込みをさせる reload はなくて 代りに graceful というオプションが用意されている.

# /usr/local/apache/bin/apachectl start
/usr/sbin/apachectl start: httpd started

以上でみなさんのお手元でも うまく Apache サーバが動き出したはずだが, いかがだろうか.

Part 3: 覚えておきたいApacheの運用法

これまでのところで, Apache サーバが起動することは確認できたし, ブラウザでアクセスしてみれば なんらかのページが見えるところまではこぎつけた. しかし, これだけでは終らない. 実際, Apache サーバを起動させるだけであればそう難しくはない. ご覧いただいた通りである. この手の, いわゆるサーバ管理がたいへんなのは とりあえずの設定が終ってからの運用である(この点は UNIX だろうが MS-Windows だろうがそう変わらないはずだ).

ここではそうした運用の基礎であり, また多くのサイトで必要となるであろう 基本的な設定について解説することにする.

コンフィグレーションファイル概説

さて, ソフトウェアの設定といえば コンフィフレーションファイルの読み書き 行き着くことが多いのであるが, Apache もその例にならっている. 最近では後述の Comanche のような GUI による設定ツールもでてきているが, まだそれほど一般的ではないようである.

基本の書式

Apache のコンフィグレーションファイルの中では 2 種類の書式にしたがって記述がなされている. 一つ目は次のようなものである.

パラメータの名前  パラメータの値

もう一つの書式はこのようなものだ.

<パラメータの名前 パラメータの値>
  …
</パラメータの名前>

ここで「パラメータの名前」のことを 特にディレクティブと呼んでいるが, そのディレクティブに対して なんらかの値を設定しているのが前者の書式である. これに対して, 後者の書式はある設定の有効範囲を <ディレクティブ>〜</ディレクティブ> の間に 制限するためのものとなる. 例を一つ示そう.

DocumentRoot /var/www/main

<VirtualHost 10.0.0.1>
  DocumentRoot /var/www/vhost
  …
</VirtualHost>

これは DocumentRoot ディレクティブの 有効範囲が VirtualHost ディレクティブによって 制限されていることを意味している. VirtualHost ディレクティブは バーチャルホストを設置するためのもので, 詳しくは後述するが, この記述に対応するバーチャルに関する リクエストを処理する際には このディレクティブの中に書かれた内容が有効となるのである. つまりこの例に示したバーチャルホストにおいては DocumentRoot とというパラメータが /var/www/vhost であるが, その他のバーチャルホストがもしあれば, それらのバーチャルホストにおける DocumentRoot の値はまったく別のものになるであろうし, バーチャルホスト以外については /var/www/main がその値となるわけである.

このようなディレクティブは他にもあって Directory, Files, Location などが やはり同様の機能を持っている. これははそれぞれ指定されたディレクトリ以下, 指定されたファイル, 指定された位置に対して 内包するディレクティブの有効範囲を限定する働きをする.

ファイルの在処

さて, それでは実際の コンフィグレーションファイルを見てみよう.

インストールの節で説明したようなやり方で Apache をインストールしていれば, しかるべきところにコンフィグレーションファイルの 雛方もインストールされているはずである. すべて手作業にてインストールした場合には configure スクリプトを --show-layout 付きで 実行した際に sysconfdir として表示された ディレクトリにコンフィフレーションファイルが置かれる. 特に --prefix を指定しなければ /usr/local/apache/conf となっているだろう.

またバイナリパッケージを利用した場合には, そのパッケージの作られ方によって位置が変ってくる. Red Hat Linux 7.0 や Vine Linux 2.0 では /etc/httpd/conf が, Debian GNU/Linux 2.2 では /etc/apache が, FreeBSD では [...] が, そうしたディレクトリとして 指定されている.

みなさんの環境に応じたディレクトリにて 次のようなファイルがあることを 確認していただきたい.

この三つのファイルの内の後の二つは内容としては空のことがある. これは Apache のバージョン 1.3.11 あたりで コンフィグレーションファイルに関するポリシーが変ったためだ. 従来は上の 3 つのファイルにまたがって 設定を行っていたのだが, 1.3.11 からは httpd.conf にまとめて書くというスタイルが 推奨されるようになった.

それによって Apache サーバの挙動も 少し変更されている. 従来は srm.conf や access.conf を 読み込ませないようにするために ResourceConfigAccessConfig の 2 つのディレクティブを使って 以下のような設定をする必要があった.

ResourceConfig /dev/null
AccessConfig /dev/null

ResourceConfigAccessConfig は それぞれ srm.conf と access.conf のファイル名を コンパイル時の設定と違ったものにしたいときに 特に設定するディレクティブで, このようにしておかないとエラーになってしまっていたのである. しかしバージョン 1.3.11 からは srm.conf や access.conf を読み込ませたくなかったら 単にそれらのファイルを削除すればよいことになった (もちろん従来の方法でも問題はない).

Apache のマニュアルの読み方

ここまでにはすでに数種類のディレクティブが登場しているが, これですべてというわけでは当然ない. どのようなディレクティブがあるのかを知りたければ Apache に付属のドキュメントにあたるのがよいだろう. ソースから手でインストールした人であれば, ソースの中の htdocs/manual/mod/directives.html を適当なブラウザで見ることができる. また, 各種パッケージを利用した場合には 以下でマニュアルを見ることができるはずだ.

Apache の機能の節でも触れたが, Apache のすべての機能は モジュールという形で提供されている. 各ディレクティブはそれらモジュールの 挙動を決めるパラメータを設定するものだと考えることができる. その意味ではディレクィブをモジュール毎に 見ていった方が都合が良い場合もあるだろう. そんなときには htdocs/manual/mod/index.html を見るとよい. このファイルには各モジュールの 名前と対応する役割りがリストされていて, かつ, 各モジュールの詳細な説明が 書かれたファイルへのリンクが張ってある.

どちらのファイルからたどって見るにせよ, 最終的には個々のモジュールについて説明した ファイルに行き着くようになっている. それらの構成は基本的に同じで, まず冒頭でそのモジュールの概要やものによっては モジュールの目的や利用方法が述べられている. 続いてディレクティブの一覧があり, その下からが個々のディレクティブの解説である.

個々のディレクティブについての記述は たとえばこのような形になっている.

AddHandler

Syntax: AddHandler handler-name extension extension...
Context: server config, virtual host, directory, .htaccess
Override: FileInfo
Status: Base
Module: mod_mime
Compatibility: AddHandler is only available in Apache 1.1 and later

AddHandler maps the filename extensions extension to the handler
handler-name.  This mapping is added to any already in force,
overriding any mappings that already exist for the same
extension. For example, to activate CGI scripts with the file
extension ".cgi", you might use:

    AddHandler cgi-script cgi

Once that has been put into your srm.conf or httpd.conf file, any
file containing the ".cgi" extension will be treated as a CGI
program.

See also: Files with multiple extensions

最初にあるのがディレクティブの名前だ. それからディレクティブの書式(Syntax), このディレクティブを書くことができる場所(Context), .htaccess ファイル(後述)でパラメータを変更する際に AllowOverride ディレクティブ(後述)によって 何が許可されていればよいのか(Override), そのディレクティブの重要度(Status), そのディレクティブを含んでいるモジュールの名前(Module), どのバージョンから使えるようになったのか(Compatibility)と いった内容が続く. この例にはないが Default という項目があることもあり, そのディレクティブに関するデフォルト値を意味する. これらの後になってようやくディレクティブについての 解説が行われる.

いくつかの項目について, もう少し詳しく解説しよう.

まず Context であるが, これには次のような値が現れる得る.

server config

これは Apache サーバ自身のコンフィグレーションファイル (httpd.conf, srm.conf, access.conf)の中で, かつ VirtualHostDirectory などの外でだけ記述できる.

virtual host

VirtualHost の中にだけ記述できる. ちなみに VirtualHost 自身の Context は server config となっているから, 結果的にはサーバのコンフィグレーションファイルの中で, かつ VirtualHost の中でのみという意味になる.

directory

Directory, Location, Files のいずれの中でだけ記述できる.

.htaccess

.htaccess ファイルの中でのみ記述できる.

Context にはこれらの値が複数個ならべられる場合もあるが, その場合にはそこにリストされている場所であれば どこにでも書けるという意味になる.

最後の .htaccess については補足が必要だろう. .htaccess ファイルというのはサーバのコンフィグレーションの一部を 別の値に置き換えるための記述をするファイルである. Apache サーバはブラウザなどからのリクエストにこたえて 情報を提供しようとする際に, その情報源となるファイルにいたるパスのどこかに .haccess という名前のファイルがないかどうかをたしかめる. もしも見つかったらその内容を読み込んで, その内容に従った動作をしようとするのである. ただし .htaccess ファイルの内容が有効である範囲は その .htaccess ファイルが置かれているディレクトリの 配下に限定されている. このようなファイルによって一部の設定を変えられることによって 一般には次のような利点がでてくると考えられる.

一方で次のような欠点もある.

実は後者についてはちゃんと解決策が用意されている. なにをかくそう, 解決策に関係してくるのが Override の値である.

少しややこしい話になるが, 各ディレクティブは, そのディレクティブがどのモジュールに入っているのか という意味での分類の他に, それがどのような種類の設定に関わるのか という意味での分類がなされている. たとトば, アクセス制限に関するものは Limit というグループに入れられているし, ユーザ認証に関するものは AuthConfig というグループに入れられている. そして Override の値は, そのディレクティブがどのグループに所属しているかを 示すものなのであり, AddHandler の例について言えば FileInfo というグループに属しているということになる.

Context に .htaccess が含まれているディレクティブは .htaccess ファイルに記述することができる. ただし, その記述が有効に働くのは サーバでの設定でそのディレクティブが属するグループについての 変更が許されている場合のみになるわけだ. それを設定するのが AllowOverride ディレクティブである. AllowOverride に指定されたグループに属する ディレクティブの値は .htaccess の中で変更することができる. 逆に言うと AllowOverride で指定されていないわけであるから, 先の例のような問題にならないように設定しておく必要がある.

多少話の範囲が広ってしまったが, 以上が Apache のディレクティブに関する マニュアルの読み方ということになる. 以降では実際のコンフィグレーションファイルの内容にも 触れてゆくので, 参考にしてほしい.

設定内容の検証

行った設定については 実際の動作で確認しなくてはならないが, 事前に最低限の書式のチェックなどを行うことならば可能である.

$ /usr/sbin/apache -t
[Wed Sep 13 12:51:26 2000] [error] Cannot resolve host name www.arika.org --- ignoring!
Syntax OK

これは Debian 環境でテストを行ってみた結果であるので /usr/sbin/apache としているが, Red Hat Linux や Vine Linux などの環境では /usr/sbin/apache ではなく /usr/sbin/httpd としてほしい. なお, この例では, メッセージからコンフィグレーションファイル中に書かれている ホスト名(www.arika.org)が間違っているのがわかる.

$ /usr/sbin/apache -t
Syntax error on line 268 of /etc/apache/httpd.conf:
Invalid command '<VirtualHost>', perhaps mis-spelled or defined by a module not included in the server configuration

これは <VirtualHost> に関してスペルミスや書式上の誤りが あるということを検出している例である. 書式上の問題がなければ次のように出力される.

$ apache -t
Syntax OK

また -t 以外のいくつかのオプションによっても コンフィグレーションなどの確認が可能である. たとえば -S を指定すると, 設定されているバーチャルホストのリストを表示してくれる.

$ apache -S
VirtualHost configuration:
211.18.217.219:80      is a NameVirtualHost
                       default server arika.org (/etc/apache/httpd.conf:271)
                       port 80 namevhost arika.org (/etc/apache/httpd.conf:271)
                       port 80 namevhost rb.arika.org (/etc/apache/httpd.conf:274)

バーチャルホストの設定については後で詳しく解説するが, この例からは次のことが確認できる.

設定というのとは少し違うが, -l オプションを指定するとことによって 組み込まれているモジュールのリストを確認することもできる.

$ apache -l
Compiled-in modules:
  http_core.c
  mod_so.c
  mod_macro.c
suexec: enabled; valid wrapper /usr/lib/apache/suexec

ただし, これは Apache サーバ内に静的に組み込まれた モジュールをリストしているだけで, 動的に組み込むモジュールについてはリストしないことに注意してほしい.

では続いて各ディストリビューションの デフォルトの設定がどうなっていのかを調べてゆこうと思う.

ディストリビューションごとのデフォルトコンフィグレーション

特にパッケージを利用した場合に言えることだが, どこに何があるのかという点, それからデフォルトのコンフィグレーションがどうなっているのか については把握しておく必要があるだろう.

インストール直後に起動ができるというのは 先ほど確認した通りであるが, その状態から何をどのように変更すればよいのだろうか. 以下ではそのあたりについてのヒントとなるであろう 事柄をディストリビューションごとに紹介してゆこうと思う.

Red Hat Linux 7.0 のコンフィグレーション

もっとも基本となるコンフィグレーションファイルは httpd.conf である. Red Hat Linux 7.0 の Apache パッケージにおいては このファイルの在処は /etc/httpd/conf/httpd.conf だとして作られている. その httpd.conf の中から主なコンフィグレーションを抜き出してみた.

ServerRoot /etc/httpd
PidFile    /var/run/httpd.pid
ErrorLog   /var/log/httpd/error_log
CustomLog  /var/log/httpd/access_log common

User  apache
Group apache

DocumentRoot   /var/www/html
ScriptAlias    /cgi-bin/ /var/www/cgi-bin
DirectoryIndex index.html index.htm index.shtml index.php index.php4 index.php3 index.cgi

#AddHandler cgi-script .cgi
AddType text/html .shtml
AddHandler server-parsed .shtml

AccessConfigResourceConfig についての記述はないが, そのデフォルト値は conf/access.conf と conf/srm.conf である. /etc/httpd/conf 以下には access.conf も srm.conf もあるが 実質的な内容は空となっている.

前半の ServerRoot〜Group はいずれも Apache サーバの管理的側面に近い設定になる. 一方後半は HTTP サーバとして 人々に提供する情報をどう扱うかについての設定である.

最初の ServerRoot ディレクティブには httpd.conf 以外のコンフィグレーションファイルや ログファイルの起点となるディレクトリを指定する. Red Hat 7.0 の場合は /etc/httpd に設定されている. ここでたとえば ErrorLog の設定が Apache サーバーのデフォルトである logs/error_log になっていれば, 発生したエラーについてログは /etc/httpd/logs/error_log に書き込まれる(起点が/etc/httpd で, logs/error_log はそこからの相対パスというわけだ)ところなのであるが, その下にあるように ErrorLog は絶対パスで /var/log/httpd/error_log と設定されているため 実際にはそうはならない. これについては CustomLogPidFile についても同様だ.

ちなみに CustomLog は各種の情報 (一般にはいわゆるアクセスログとしての情報だが)を おしきせではなく自由な形でログに記録するようにする ディレクティブであり, PidFile はその名前からわかるように Apache サーバのプロセス ID が収められるファイルを 設定するディレクティブである.

その下に続く User および Group は Apache サーバが動作するときの権限を指定するものだ. HTTP サーバが配置されるのは一般に 80 番ポートだということは すでに述べた通りだが, このためには Apache サーバに root 権限が必要となる. そのため Apache サーバが root 権限で 起動されることもしばしばなのであるが, あらゆる動作を root 権限の下で行うのは大変危険である. そこで適当なタイミングで root 権限を放棄し, User および Group で指定された権限で 動作するようなつくりになっている.

以降はのディレクティブはこうだ.

HTTP サーバの動作の節で リクエストを受けた HTTP サーバは リクエストに含まれるパス情報を ホスト内のあるディレクトリ領域にマップして, パスに対応するファイルか得た情報を 元にレスポンスとして返すという意味のことを述べた. DocumentRoot ディレクティブが その「あるディレクトリ領域」の起点を指している. たとえば http://www.linux.or.jp/softwarereview/index.html というページにだれかがアクセスしようとすると, 彼女の使っているブラウザは ホスト www.linux.or.jp に接続した上で 「/softwarereview/index.html なる情報をください」と お願いするわけだが, これを受けた www.linux.or.jp 上の Apache サーバは おおざっぱにはこのように動く. まず DocumentRoot に指定されている /home/httpd/html を起点に そこからの相対パスで softwarereview/index.html が あるかどうかを確認する. さらに Apache サーバの権限(つまり User, Group で 指定された権限)において, そのファイルを読めるかどうかも確認する. すべてがクリアされればファイルの中身を レスポンスとしてブラウザに返してやる. 実際にはもっと多くのことをこなしているわけだが, あらましとしてはこのようなところである.

ScriptAliasAddHandler については Part 3: 覚えておきたいApacheの運用法で詳しく述べることにして, 残った DirectoryIndex について解説してしまおう. Apache サーバはディレクトリに対するリクエストを受け取ると, このような動作をする. つまり, そのディレクトリの中に DirectoryIndex に列挙してあるファイルがあるかどうか, DirectoryIndex にリストされている順番に探してゆく. 結果, いずれかのファイルが見つかれば それを要求された情報であると見倣して, その中身を元にレスポンスを返す. もしも DirectoryIndex にリストされたファイルが どれもなければ Apache サーバが自力で ディレクトリ内のファイル一覧を生成するか, あるいはエラーにする(この点は設定依存).

Vine Linux 2.0 のコンフィグレーション

Vine Linux 2.0 の場合は次のような設定がデフォルトだ.

ServerRoot /etc/httpd
PidFile    /var/run/httpd.pid
ErrorLog   /var/log/httpd/error_log
CustomLog  /var/log/httpd/access_log common

User  nobody
Group nobody

DocumentRoot   /home/httpd/html
ScriptAlias    /cgi-bin/ /home/httpd/cgi-bin/
DirectoryIndex index.html index.htm index.shtml index.cgi

#AddHandler cgi-script .cgi
AddHandler server-parsed .shtml

コンフィグレーションファイルの位置や ログファイルの位置については Red Hat Linux 7.0 と同じであるが デフォルトの DocumentRootDirectoryIndex が異っている. また Red Hat Linux 7.0 では User, Group に Apache サーバ専用のものを設定していたのに対して Vine Linux では一般的な「だれでもない」ユーザや グループを指定しているのも異っている点である.

ちなみに Red Hat Linux 6.2 のデフォルトもこれとほぼ同じであるから, Red Hat Linux 6.2 を使っていたユーザが 7.0 に移行する際には とまどうことがあるかもしれない.

Debian GNU/Linux 2.2 のコンフィグレーション

続いて Debian GNU/Linux の場合. 前述した通り Debian GNU/Linux の Apache パッケージでは httpd.conf の位置は /etc/apache/httpd.conf として作られている. access.conf や srm.conf の位置も同様に /etc/apache/access.conf などとされている. 他のディストリビューションとは異なり 設定内容は access.conf や srm.conf にも書かれている(1.3.12 をベースとしている woody の Apache パッケージでは一つにまとめられている).

ServerRoot /etc/apache
PidFile /var/run/apache.pid
ErrorLog /var/log/apache/error_log
CustomLog /var/log/apache/access.log common

User www-data
Group www-data

DocumentRoot /var/www
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
DirectoryIndex index.html

#AddHandler cgi-script .cgi
#AddHandler server-parsed .shtml

AddDefaultCharset on
AddDefaultCharsetName iso-8859-1

Debian GNU/Linux と他のディストリビューションとの 最も大きな相違は AddDefaultCharset などの設定と言えるだろう.

まず注意事項だが, オリジナルの Apache バージョン 1.3.9 には AddDefaultCharset, AddDefaultCharsetName といった ディレクティブは存在していない. これはセキュリティ上の問題をクリアするために バージョン 1.3.12 で実装された機能を パッケージのメンテナが あえて 1.3.9 に持ち込んだものである*8. また AddDefaultCharsetName というディレクティブは Apache バージョン 1.3.12 にも存在しない. オリジナルの Apache で実装されている AddDefaultCharset は 以下のような条件のときに, Apache サーバに AddDefaultCharset で指定された情報を付加させるための ディレクティブである.

この機能はデフォルトで off, すなわちそのような 処理を行わないようになっているが, AddDefaultCharseton を設定するか, 文字コード情報を直接指定するとこの機能が働きだす. なお単に on とした場合には iso-8859-1 という文字コード情報が付加されるという仕様だ *9.

対する Debian GNU/Linux 2.2 の Apache パッケージの実装では AddDefaultCharset では on または off のみを指定し, AddDefaultCharseton のときに 付加する文字コード情報を AddDefaultCarsetName にて指定する というものとなっている. 設定方法にはこのような相違があるが, この件に関連する Apache サーバの動作そのものは バージョン 1.3.12 のそれとまったく同じである. また AddDefaultCharset のデフォルトの値は off, AddDefaultCharsetName のデフォルト値は iso-8859-1 と, この点においても実質的にはバージョン 1.3.12 と同じであると言える. しかし, 上記のように httpd.conf でのデフォルトの 設定では AddDefaultCharseton, AddDefaultCharsetNameiso-8859-1 と なっているため注意が必要でなのある.

くり返しになるが, バージョン 1.3.12 において このようなディレクティブが追加されたのは, レスポンスとしてドキュメントを返す場合には セキュリティ上の観点から そのすべてに正しい文字コード情報を 付加しておくべきだとされているためだ. しかし, その設定が不完全であると ブラウザで見た際に文字化けが起こるなどの 問題が新たに発生する可能性がある. インストールしたばかりで この点に関する設定がなされていない Debian 環境で日本語のページを作り, そのページをブラウザで見てみるとわかるはずだ. なお, 他のディストリビューションでも 文字コードに関する設定が特にない状態で AddDefaultCharset を設定すると 同じようなことが起るのを確認できるはずだ. これは, どのような言語・文字コードで書かれたドキュメントであっても そうしたいわば不完全な設定のおかげで 「文字コードは ISO-8859-1 である」という でたらめの付加情報とともにページの内容が送られてしまうためだ. そこで, ここからはすべての場合に共通の話になるが, AddCharset ディレクティブを使って 正しい文字コード情報を付加できるようにしておきたい.

AddCharset は次のように使う.

AddCharset 文字コード情報 拡張子...

文字コード情報には ISO-20220-JP とか EUC-JP のような文字コードを書き, 拡張子には .html などを書く.

AddCharset ISO-2022-JP .html
AddCharset SHIFT_JIS .htm

この例では .html という拡張子を持つファイルについては ISO-2022-JP という文字コード情報が付加されるし, .htm という拡張子を持つファイルについては SHIFT_JIS という文字コード情報が付加される. つまり, この設定がなされているサーバにおいては 拡張子が .html のファイルは ISO-2022-JP(おおざっぱにいうと JIS コード)で, 拡張子が .htm のファイルは シフト JIS コードで 書いてやると文字コード情報と実態が 正しく一致するということになる.

ただし, このような設定を http.conf などに書いてしまうと そのサイト内のすべてのファイルの文字コードを そろえなければならなくなってしまう. さいわい AddCharset.htaccess ファイルに書くことができるから, AllowOverride で FileInfo の書き換えを許可して 適宜設定を行うというようにするのも一つのやり方であろう (つまりマニュアルの AddCharset の項の Override の欄には FileInfo と書いてある). またもう一つの方法に content negotiation という機能を 使うというやり方もあるのだが, その詳しい説明はもう少し後の節で行うことにする.

FreeBSD のコンフィグレーション

(おまかせ)

…続く次節では SSI や CGI などを使った 動的に生成されるページを作るために 必要となる事柄についての話をしたい.

コラム: AddCharset と Debian の Apache パッケージ

ちょうどこの原稿を書いているときに Debian GNU/Linux 2.2 の Apache パッケージ(パッケージの バージョンは 1.3.9-13.1)についての不具合が見つかった.

その内容は AddDefaultCharset 機能のバックポートが 行われたにもかかわらず, AddCharset については バックポートされなかったというものである. この不具合のために AddDefaultCharset を on にした場合 そのサーバで提供されるページの中で 英語以外で書かれているページを うまく表示できなくなってしまう.

この問題はいずれ修正されるであろうが, その間をしのぐために, この問題を修正するパッチを筆者の方であてて作ったパッケージを 付録 CD-ROM に収録しておいた. よければ動作をよく認確の上で使ってほしい.

なお, AddDefaultCharset を off にしておけば ディストリビューションに関係なく 一連の問題を回避できるのではあるが, それは表面的なものであり 例のセキュリティ上の問題に加え, さらに一部のブラウザの奇妙なふるまいによって 別の問題が引き起されてしまう場合すらある.

やはり正しい文字コード情報を付加すすのが 正しいやり方であると思われるので Debian GNU/Linux 2.2 のコンフィグレーション の節の後半部分などを参考に, 正しい設定を行っておこう.

コラム: その他の Linux 系ディストリビューションについて

その他の Linux 系ディストリビューションについての情報をまとめておく.

Kondara MNU/Linux 1.2

バージョン 1.3.12 の Apache をベースとしたパッケージが提供されている. パッケージの確認方法やインストール方法は Vine Linux 2.0 と基本的に同様で, アップデート情報やセキュリティに関する情報は <URL:http://www.kondara.org/errata/> から得られる. ネットワークでのパッケージの取りよせは <URL:http://www.kondara.org/ftplist.html> にあるミラーサイトの中から適当なサイトを選んで行うとよい.

デフォルトのコンフィグレーションも Vine Linux 2.0 とほぼ同じであるが, 一部日本語環境向けの設定がないといった違いがある.

TurboLinux 6.1

バージョン 1.3.12 の Apache をベースとしたパッケージが提供されている. パッケージの確認方法やインストール方法は Vine Linux 2.0 と基本的に同様で, アップデート情報やセキュリティに関する情報は <URL:http://www2.turbolinux.co.jp/users/fuse/support_res.php3?product=ju&prokey=4> から得られる. ネットワークでのパッケージの取りよせは <URL:ftp:://ftp.turbolinux.co.jp/pub/TurboLinux/> より.

デフォルトのコンフィグレーションは Apache のソースに同梱されているものとほとんど同じ. ServerRoot は /etc/httpd, DocumentRoot は /home/httpd/html になっている. 動作時の権限は nobody/nogroup である.

Laser5 Linux 6.2

バージョン 1.3.12 の Apache をベースとしたパッケージが提供されている. パッケージの確認方法やインストール方法は Vine Linux 2.0 と基本的に同様で, アップデート情報やセキュリティに関する情報は <URL:http://www.laser5.co.jp/support/csm/update/62/index.html> から得られる. ネットワークでのパッケージの取りよせは <URL:http://www.laser5.co.jp/download.htm> にあるミラーサイトより.

デフォルトのコンフィグレーションも Vine Linux 2.0 とほぼ同じであるが, 一部日本語環境向けの設定がないといった違いがある.

動的ページの運用

Web サイトになくてはならないものの一つは 広い意味での動的コンテンツであろう. 最新では動的にページを構築するための 様々な新しい技術が登場してきているが, 従来からの SSI や CGI といった仕組みが 利用される機会もまだまだ多い.

以下では SSI と CGI に焦点をしぼり, これらの技術による動的ページを Apache サーバによって運用する方法について 解説してゆくことにする.

SSI

SSI というのは Server Side Includes の略である… と言ってもピンとこないが, これは次のような機能を実現するための仕組みのことだ.

まず, Apache サーバを介して提供する情報の 元となるドキュメントファイルの中で, ある特殊な書式に従った記述(そのような記述のことを SSI コマンドなどと呼ぶこともある)をしておく. Apache サーバはブラウザからのリクエストに応じて情報提供すべく リクエストに対応するサーバ内のファイルを特定し, これをもってブラウザにレスポンスを返すわけだが, この際, そのファイが SSI 機能を有効にすべきである とされているファイルであった場合に 普段とは違った動きをする. つまり, ファイル内にある SSI コマンドを Apache サーバの中で解析し, SSI コマンドが書かれている部分を その結果によって適宜置き換えを行った上で ブラウザに返してやる(図 5).

結果的にブラウザの元へ届けられる情報は サーバ内に置かれているファイルそのものの内容ではなく, その内容を元に動的に生成されたものとなるわけだ.

この機能によって対話的なページを作ることは 基本的にはできないが, たとえば次のようなことなら簡単にできる.

通常はドキュメントの中の一部分を動的に変更あるいは 調整する目的で使われることが多いようだ.

さて, こうした SSI 機能を利用するためには いくらかの準備が必要となるので, まずはそこから始めよう.

モジュールをロードする

これまではモジュールの扱いについて, あえて触れてこなかった. Apache サーバが, 自身の機能を適当に 分割してモジュール構成にしているということは すでに述べた通りである. こうしたモジュールは, Apache サーバを構築する際に 静的に組み込んでおくこともできるし, Apache サーバを起動するタイミングで 動的に組み込むこともできる*10. また, 静的に組み込まれているモジュールであっても その機能を働かないようにしておくことも可能だ.

モジュールの動的な組み込みは LoadModule というディレクティブによって 行うことができる. 書式はたとえばこうだ.

LoadModule includes_module /usr/lib/apache/1.3/mod_include.so

LoadModule の Context は server config となっており, これは httpd.conf の中に記述できるということである. 従ってこのような記述は httpd.conf にて行う. 実は SSI 機能は includes_module という モジュールの中におしこめられている. 上の例は Debian 環境において SSI 機能を有効にするために includes_module を組み込んもうとしたものである. Debian 環境のデフォルト設定では includes_module は 組み込まれないようになっているため, Debian 環境で SSI 機能を使うためには このような httpd.conf の調整が必要となる.

一方, Debian 環境以外についてどうかと言うと, Red Hat Linux 7.0 をはじめとする いわゆる RPM 系のディストリビューションでは いずれもデフォルトの設定で includes_module 組み込んでいるため このような設定は必要ない. ただし RPM 系のディストリビューションの モジュール関係の設定にはあるトリックが施してある. トリックといってもディストリビューション 独自の特殊なことを行おうというのではない. ClearModuleList というディレクティブを利用して, 有効であるモジュールのリストをいったんクリアし その後に改めてモジュールを有効にしている というのがそのトリックの内容だ. このため, 後からモジュール関係の 設定を追加する際には注意が必要だ.

(FreeBSD については…?)

以上で Apache サーバ自体は SSI 機能を使えるようになったのだが, まだこれだけでは動き出さない. さらなる設定が必要となる.

ハンドラの設定

Apache サーバがレスポンスを返すとき, 通常は対応するファイルの内容を そのまま素通しで送り出す. これの動作は当り前なのではあるが SSI 機能を使おうとする場合には これでは困ってしまう. 送り出してしまう前に, その記述に応じた しかるべき変更を加えてやらなくてはならないからだ.

このようなケースに役立つのがハンドラだ.

Apache サーバは情報を送り出す際に その元となるファイル(など)に 割り当てられたハンドラを使用している. ちょうど図 5 の斜線部分に位置するのが ハンドラだと考えればよいだろう. 適要するハンドラが特に指定されていない場合には, 内容を素通しするデフォルトのハンドラが使われる. この意味では図 5 は 図 6 のように書き直すべきだろう.

さて, ここでは SSI 機能を使いたいわけであるから, SSI コマンドを記述しているファイルに対しては SSI 用のハンドラを割り当てたい. SSI 用のハンドラは前節で組み込んだ includes_module の中にあって, その名前は server-parsed だ. ファイルにハンドラを割り当てるには AddHandler* ディレクティブを使い, 次のようにする.

AddHandler server-parsed .shtml

これによって拡張子が .shtml であるすべての ファイルに SSI ハンドラである server-parsed が割り当てられたことになる. また AddHandler の Context は server config, virtual host, directory, .htaccess となっており, httpd.conf だけでなく .htaccess でも設定できるし Directory ディレクティブや Location ディレクティブを使うなどして 影響を与える範囲を限定することもできる.

<Directory /var/www/ssi_test>
  AddHandler server-parsed .shtml
</Directory>

この例では /var/www/ssi_test 以下にあって, かつ拡張子が .shtml であるファイルについてのみ server-parsed ハンドラを割り当てたことになる. このように Directory ディレクティブは <Directory /dir/name></Directory> の間に書いた設定を /dir/name 以下にだけ適要するという 働きを持っている. また Location という似たような 働きをするディレクティブもあって, こちらはリクエストされた URL に対して働く. たとえば DocumentRoot/var/www であれば, 以下の記述は先の Directory の 例と結果的に同じ意味になる.

<Location /ssi_test>
  AddHandler server-parsed .shtml
</Location>

ハンドラを割り当てるために必要な 設定は以上で終りだが, SSI 機能を活かすにはもう少し設定が必要だ.

オプションの設定

SSI のためのモジュールでは 特に SSI を有効あるいは無効にするためにスイッチを設けている. このスイッチを入れておかないと, たとえハンドラが割り当てられていても SSI 機能は動き出してはくれない. このスイッチを入れて SSI 機能を働かせるために httpd.conf に必要となる記述は 次の通りである.

Options +Includes

この記述をコンフィグレーションファイルの 適当な位置――AddHandler を記述した Directory の中などに書いておく. + によって Includes オプションを有効としている. もしもオプションを無効にしたければ(スイッチを切りたければ) 次のように - を指定することができる.

Options -Includes

このような例を考えてみよう.

<Directory /var/www/ssi_test>
  AddHandler server-parsed .shtml
  Options +Includes
</Directory>

<Directory /var/www/ssi_test/non_ssi>
  Options -Includes
</Directory>

この設定によれば /var/www/ssi_test 以下にある .shtml ファイルについては server-parsed ハンドラが割り当たっていて SSI 機能が有効になる. またそのサブディレクトリについても 同様の効力を発起するが, サブディレクトリの一つである /var/www/ssi_test/non_ssi においはそうではない. これは - 付きの Includes によって ここでだけオプションが無効になっているからだ. なお Options+- も付かないオプションを指定すると, その機能だけが有効となるので注意が必要である.

<Directory /X>
  Options +A +B +C # 有効なオプションは A, B, C
</Directory>

<Directory /X/Y>
  Options -A +D    # 有効なオプションは B, C, D
</Directory>

<Directory /X/Y/Z>
  Options B E      # 有効なオプションは B, E
</Directory>

さて, これで SSI 機能も働くようになったわけだが, 必要な設定がこれですべてかというと… 残念ながら設定はもう少し続くのである.

MIME タイプの設定

SSI のための server-parsed ハンドラは 基本的に SSI コマンドによる ドキュメントの内容の一部書き換えを 行ってくれるだけで, それ以上のことはしてくれない. それで何が困るのだ? と思われるかもしれないが, 実際に困ってしまうのである. これはここまでに挙げた設定を行った上で /var/www/ssi_test/test.shtml なる 次のような内容のファイルを用意して 実験してみよう. SSI コマンドと呼ばれるのは中ほどにある <!--#echo var="DATE_LOCAL" --> である. これはそのファイルにアクセスがあった時刻を この部分に展開するという意味になる.

<HTML>
 <HEAD>
  <TITLE>SSI TEST</TITLE>
 </HEAD>
 <BODY>
  <!--#echo var="DATE_LOCAL" -->
 </BODY>
</HTML>

そしてこのファイルに対応する URL に Netscape Navigator を使って アクセスしてみた様子を示しているのが図 7 だ.

[SSI の設定の失敗例]
図 7: SSI の設定を失敗してしまった

みなさんの想像とは異った結果になっているのではないだろうか. ご覧の通り HTML で書かれたページとして解釈されず, ただのテキストとして扱われてしまったようである.

これにはもちろん理由がある. Apache サーバは .shtml ファイルが どのような形式のファイルであるのか まったく知らないというのがその理由だ.

通常 Apache サーバは 各ファイルの拡張子に応じて そのファイルの形式を決定し, 決定したファイル形式を MIME タイプという形で 付加情報としてブラウザに送っている. たとえば HTML 形式のテキストファイルであれば text/html という表現になるが, これをファイルの内容とともにブラウザに送っているわけだ. text/html という MIME タイプを判って送られた情報は, ブラウザによって HTML 形式のテキストであると判断され HTML 形式に合ったやり方での表示が成される. また JPEG 形式のイメージファイルであれば MIME タイプは image/jpeg になるが, これを受けたブラウザはそれをイメージとして表示しようとする といった具合である.

こうした拡張子と MIME タイプの対応については 何の設定されていないように見えるかもしれないが, 実はひそかに設定されてる. 関係するのは TypesConfig ディレクティブだ. このディレクティブには MIME タイプとそれに対応する拡張子のリストが書かれた ファイルを指定する. Apache サーバはそれを読み込んで それぞれのファイルに応じた MIME タイプを設定するのである.

さて, 問題なのは .shtml が何を意味するのかについて Apache サーバが知らないということであった. となれば Apache サーバに適切な解答を教えてあげなくてはならないだろう. その目的のためには AddType ディレクティブを使う.

AddType MIME/タイプ 拡張子

これは拡張子に対して MIME/タイプを 対応させるという意味になる. 従って .shtml ファイルを 正しく HTML 形式であると認識させるための 実際の設定はこうなるだろう.

AddType text/html .shtml

このような設定をした上で先のテストをもう一度やってみた様子を 図 8 に示しておこう.

[SSI の設定完了後]
図 8: SSI の設定を終えた

試してみよう

以上までで SSI 機能のために必要な設定はすべて完了した. やや長い道のりとなってしまったので, 設定内容をまとめておこう.

<Directory /var/www/ssi_test>
  AddHandler server-parsed .shtml
  AddType text/html .shtml
  Options +Includes
</Directory>

これによって /var/www/ssi_test 以下にあって 拡張子が .shtml であるファイルについては SSI コマンドの解釈と置換が成されるわけである.

では, ここからテストを兼ねて, SSI 機能の一部をお目にかけよう. その前に SSI コマンドの書式を説明しておこう. SSI コマンドは次のように <!--# で始めて --> で終える. # に続く最初の部分が SSI コマンドで, それに続くのは SSI コマンドに与えられる オプション引き数である.

<!--#コマンド オプション1 オプション2 … -->

すべてのコマンドは Apache サーバの権限で 実行されることに注意が必要である. つまり User および Group ディレクティブで 設定したユーザ/グループに応じた権限で ファイルへのアクセスや コマンド実行が行われるのである.

どのようなコマンドがあるかと その動作に関するサンプルを以下にまとめてみた.

echo/set

echo はオプション var= で指定された 変数の内容を出力し, setvar= で 指定された変数に value= で指定された値を設定する.

サンプル

<OL>
 <LI>test_var: <!--#echo var="test_var" --></LI>
 <LI>test_var = 現在の時刻: 
  <!--#set var="test_var" value="${DATE_LOCAL}" --></LI>
 <LI>test_var: <!--#echo var="test_var" --></SPAN></LI>
</OL>
printenv

設定されているすべての変数の一覧を出力する. サンプルの実行例の中に echo/set の サンプルで設定した test_var が含まれているのがわかる.

ユーザが設定した以外の変数は サーバが設定してくれているものである. これらの変数は SSI を使う上で役立つことが多いので, 後で簡単にまとめておこう.

サンプル

<PRE><!--#printenv --></PRE>

図 9.1: サンプルイメージ

fsize/flastmod

file= または virtual= で指定されたファイルについて, fsize ではそのサイズを, flastmod ではその最終更新時刻を出力する.

file=

file= での指定はサーバ内でのパス名になり, この SSI コマンドを含むファイルからの相対パスであると解釈される. 絶対パスや .. を含めることはできない.

virtual=

virtual= での指定は URL でのパス名になる. つまり /DocumentRoot になる.

サンプル

<UL>
 <LI>このファイルのサイズ: <!--#fsize file="${DOCUMENT_NAME}" --></LI>
 <LI>このファイルの最終更新: <!--#flastmod file="${DOCUMENT_NAME}" --></LI>
</UL>
config

エラーメッセージや時刻などの出力形式などを調整する.

errmsg=

サーバが理解できない SSI コマンドがあった場合などに表示される エラーメッセージをカスタマイズする.

サンプル

<DL>
 <DT>デフォルトのエラー:</DT>
 <DD><P><!--#nonexist --></P></DD>

 <DT>カスタマイズしたエラー: 
  <!--#config errmsg="ERROR Occurred!" --></DT>
 <DD><P><!--#nonexist --></P></DD>
</DL>
sizefmt=

fsize の出力形式を調整する. 指定できるのは bytesabbrev で, それぞれバイト単位での出力と 適当な単位(k, m など)を判っての出力に対応する.

timefmt=

時刻に関するすべての出力について調整する. flastmod のようなコマンドだけではなく echovar に時刻を表す DATE_LOCAL, DATE_GMT, LAST_MODIFIED を指定した場合にも 反映される.

サンプル

<DL>
 <DT>デフォルトの表示:</DT>
 <DD><P>このファイルのサイズ: <!--#fsize file="${DOCUMENT_NAME}" --><BR>
   このファイルの最終更新: <!--#echo var="LAST_MODIFIED" --></P></DD>

 <DT>カスタマイズした表示: 
  <!--#config sizefmt="bytes" -->
  <!--#config timefmt="%Y-%M-%d %H:%M:%S (%Z)" --></DT>
 <DD><P>このファイルのサイズ: <!--#fsize file="${DOCUMENT_NAME}" --><BR>
   このファイルの最終更新: <!--#echo var="LAST_MODIFIED" --></P></DD>
</DL>
exec

指定されたコマンドなどを実行し, その出力を exec コマンドの出力とする.

cmd=

指定されたコマンドをそのまま実行する.

cgi=

指定されたコマンドを, 後述の CGI プログラムであるとして実行する.

サンプル

<P><CODE>ls -l</CODE> を実行:</P>
<PRE><!--#exec cmd="ls -l" --></PRE>

図 9.2: サンプルイメージ

include

指定されたファイルの内容をその場に取り込む. ファイルの指定方法には file=virtua= の二通りの方法があり, これらは fsize/flastmod の場合に準ずる.

取り込まれたファイルに SSI コマンドがあれば, それも評価するため注意が必要である. たとえば自分自身を取り込もうとすると ある時点でエラーになるといったことだ. なお, ここでのサンプルについて言えば ErrorLog で指定したログファイルに 以下のようなメッセージが残されていた.

Recursive include of "test.shtml" in parsed file

サンプル

<P>このファイルを取り込んでみる:</P>
<TABLE BORDER="1">
 <TBODY>
  <TR>
   <TD><PRE><!--#include file="test.shtml" --></PRE></TD>
  </TR>
 </TBODY>
</TABLE>

図 9.3: サンプルイメージ

printenv で表示される変数は echo コマンドをはじめとする 各コマンドの var= によって参照することができる. また, それ以外の部分や後述する条件式の中などでは ${ } でくくることで変数を参照することができる. ちょうど fsize/flastmod のサンプルで file= にあるのがそうで, DOCUMENT_NAME という変数には アクセスされたファイル自身の名前が入れられている. また DATE_LOCALDATE_GMT によってアクセスされた時刻を 得ることができるし, config のサンプルで使った LAST_MODIFIED からはファイル自体の 最終更新日を得ることができる.

printenv のサンプルには, これら以外にもたくさんの変数表示されているが, それらについては次の CGI の節の中で解説することにしよう.

次に SSI におけるフロー制御のやり方を説明する. SSI でも他のプログラミング言語にあるような if〜else〜 というような条件式を使うことができる. SSI での条件式は次のように記述する.

<H1>SSI での条件分岐</H1>

<!--#config timefmt="%Y" -->

これは
<!--#if expr="${LAST_MODIFIED} >= 2001" -->
21 世紀
<!--#else -->
  <!--#if expr="${LAST_MODIFIED} >= 2000" -->
20 世紀最後
  <!--#else -->
1900 年代
  <!--#endif -->
<!--#endif -->
に作られた作品です. 

expr= に続くのが条件式で =(等しい), !=(異なる), <(より大き), >(より小さい), <=(以上), >=(以下)を 使うことができる. 条件が成り立てば ifelse の部分を残し, 成り立たなければ elseendif の部分を残すというのは 言うまでもないであろう. また, ご覧の通り条件文をネストさせることも可能だ.

上のような記述をしたページを Netscape Navigator で見てみた様子を以下に示しておく.

[SSI での条件分岐]
図 9.4: SSI での条件分岐

簡単ではあるが SSI についての解説は以上にしたい. 引き続いて, もう一つの動的コンテンツの 生成方法である CGI についての解説をしてゆこう.

CGI

CGI は Common Gataway Interface を略したもので, その名の通りなんらかのプログラムを指すものではない. むしろやり取りのルールを定義している プロトコルと言った方がよいだろう. では CGI では何と何のやり取について 言及しているのかという話になる. それは HTTP サーバと HTTP サーバから起動される プログラムとの間のやり取りのルールについて 定義しているものである.

HTTP サーバは特定に合致するリクエストを受け取ると プログラムを起動して, そのプログラムの出力をもって ブラウザへのレスポンスとする場合がある. このとき, 起動され, そしてレスポンスを作り出すプログラムのことを CGI プログラムと呼ぶことがあるが, この呼び方の元となったのは HTTP サーバと当該プログラムの間を結ぶ CGI である(図 10). CGI で規定していることは, そう多くはない. HTTP サーバから CGI プログラムへと 必要な情報を渡すための二つのやり方と, CGI プログラムの出力のやり方についての規定があるだけで, 各々の内容も簡単なものだ.

情報の渡し方

まず二種類の情報の渡し方についてだが, これはブラウザからのリクエストの種類に対応している. CGI プログラムが呼び出される最も一般的な場面は Web ページの中に FORM とよばれる選択欄や入力欄があり, それに入力したという場合だろう. このとき入力欄のソースとなっている FORM では GET または POST のいずれかの方式を指定していて, ブラウザではそれに応じた方法によって HTTP サーバへとリクエストを送る. ページにアクセスしている人から情報を送るのに 「リクエスト」だというのはおかしなことだと思うかもしれないが, このことの真相は入力された情報を付加情報とする リクエストを送り, あくまでなんらかのレスポンスを 期待するものなのである. それはさておき, 情報の受け渡しについての話だが 次のような形になる.

GET リクエストを受けた場合

FORM の入力欄に入力された値は 環境変数 QUERY_STRING に入れられている. また環境変数 REQUEST_METHOD には GET という文字列が設定されている. CGI プログラムでは REQUEST_METHOD の内容が GET である場合には QUERY_STRING からユーザが入力した情報を得ることができる.

POST リクエストを受けた場合

FORM の入力欄に入力された値は CGI プログラムの標準入力に渡され, 環境変数 REQUEST_METHOD は 文字列 POST が設定される. CGI プログラムでは REQUEST_METHOD の内容が POST である場合には, 標準入力から環境変数 CONTENT_LENGTH に 設定されている値分だけの情報を読み込むことで ユーザの入力内容知ることができる.

また, これらの他に HEAD という形のリクエストを受ける場合もあるが, この場合については GET の場合と同様の方法で情報を受け取ることができる.

いずれの方法でも受け渡される情報は 一定の変換が施されている. 一般に URL エンコードと呼ばれる変換で, これによれば英数字と一部の記号を除いて % を前置いた 16 進数という形に変換される. このことの例外は空白であり, 空白だけは + へと変換されることになっている. たとえば % は ASCII コードで 16 進の 25 になるが, これを URL エンコードすると %25 となる. また A & B = C を URL エンコードすると A+%26+C+%3d+C のようになる.

ところで Web ページのそうした FORM には 入力欄が複数あることもめずらしくない. そのため入力欄にはそれぞれ名前を付けて区別しているのだが, CGI に渡される実際の値もこれを反映している. 一例として次の FORM によって作られる 図 11 のような入力欄になんらかの値を入力を 求めることを考えよう.

<FORM ACTION="result.cgi" METHOD="POST">
 名前: <INPUT TYPE="TEXT" NAME="name"><BR>
 年齢: <SELECT NAME="age">
  <OPTION>〜19</OPTION>
  <OPTION>20〜29</OPTION>
  <OPTION>30〜49</OPTION>
  <OPTION>50〜</OPTION>
 </SELECT><BR>
 <INPUT TYPE="SUBMIT" VALUE="送信">
</FORM>
[FORM の例]
図 10: 入力 FORM

ここで, われわれの Apache サーバから CGI プログラムとして起動されるのは, ACTION= に書かれた URL に対応する result.cgi というファイルになる. そしてリクエストの種類は POST だ. したがってブラウザで入力された値は result.cgi の標準入力から得ることができる. 実際に result.cgi の標準入力に 入っていたデータはこのようなものであった.

name=%1B$B$d$%5E$%40$%22$-$i%1B%28B&age=20%1B$B%21A%1B%28B29

一見文字化けしているのではないかとも思える内容だが, よく見てみると name=age= という文字列を見つけられる. CGI によれば FORM の中で NAME= として指定されている その各入力欄の名前をもって 名前=入力値 という形にし, さらに項目同士を & でつないだものが ひとまとまりの情報として CGI プログラムに 渡されることになっている. もちろん入力値は先の URL エンコードを施した上でだ.

このようなものを解読するためには, まず & で項目を区切り, その後で URL エンコードの逆のことをしてやればよい. つまり %xx から元のデータを復元するわけである. やや話がとんでしまうが, この復元の処理を Perl で書くとすると このようなものになるだろう(あまくで一例).

$input = 'name=%1B$B$d$%5E$%40$%22$-$i%1B%28B&age=20%1B$B%21A%1B%28B29';
# 本来は $input に適切な値を読み込んでくる処理がいる. 

@pairs = split('&', $input);
foreach $pair (@pairs) {
  ($param, $value) = split('=', $pair, 2);
  $param =~ tr/+/ /; $param =~ s/%([0-9a-fA-F]{2})/chr hex($1)/ge;
  $value =~ tr/+/ /; $value =~ s/%([0-9a-fA-F]{2})/chr hex($1)/ge;

  print "$param: $value\n"
  # 本来はこのように表示するのではなく
  # ハッシュ(連想配列)に入れるなどしておいて後でその値を使う. 
}

このスクリプトを実行するとわかるが, 筆者が図 11 の FORM に対して 「名前」欄に「やまだあきら」と入力し 「年齢」については「20〜29」を選択したのである.

なお, 先の FORM の METHOD= の値が GET であった場合, ブラウザからの入力データは 環境変数 QUERY_STRING から取り出すことができ, その形式はまったく同じになるというのは前述した通りである.

レスポンスの出力

最後に CGI プログラムからの出力について説明して, それから実験を行ってみたい.

CGI プログラムからの出力は基本的には ほとんどそのままブラウザに渡ると考えてもよい. ほとんどそのままと言ったのは図 11 に示した通り, 一般には CGI プログラムとブラウザの間に HTTP サーバが入ってしまうからだ. では HTTP サーバはこの位置で何をやっているのかというと, CGI プログラムが出力する HTTP のレスポンスに 足りないものを補ってくれているのである.

HTTP のレスポンス(リクエストも基本は同じだが)は ステータス行, ヘッダ部, ボディ部の三部構成になっている (コラム). これまでにすでに登場している文字コード情報や MIME タイプといった付加的な情報は ヘッダ部に入れられており, また HTML 形式のテキストなど 提供される情報そのものはボディ部に入れられている. 残りのステータス行には HTTP で定められたレスポンスコードなどが 含まれていなければならない. CGI プログラムは本質的には ブラウザと直結しているものであるから, ブラウザが求めているような(ある程度)正しい HTTP レスポンスを構成しなくてはならないのであるが, これは少しばかり大変だ. そこで「これだけはなくてはならないという 最低限のレスポンスヘッダだけを示してくれれば, あとはなんとかしてしてあげましょう」とがんばってくれているのが 間に入った HTTP サーバの役割りなのである.

「これだけはなくてはならない」とはいうものの, いったいどれくらいのものがそろっていれば良いのかという点についてであるが, 実のところ CGI プログラムがヘッダをまったく 出力しなかったとしても少なくとも Apache サーバの場合に 関してははエラーにはならないようだ. ヘッダ部とボディ部の区切りの空行だけは必要であるが, それだけでレスポンス行といくつかのヘッダを作って ブラウザに渡してくれるのである. ただ, 現実的にはレスポントして返した情報の MIME タイプや文字コード情報を明示しておかないと せっかくのレスポンスも使いものにならないものになってしまうので, これらの情報を含む Content-Type ヘッダくらいは CGI プログラムで出力してやるべきだろう.

CGI 変数

ここまでで CGI プログラムにユーザの入力値が 渡される際のやり方とレスポンスの返し方についてはわかっただろう. ところで HTTP サーバから CGI プログラムに渡される情報は 他にはないのだろうか? 実はまだあるのである.

SSI の節のサンプルに見られるような 環境変数は CGI プログラムにおいても参照できる. それぞれの意味は CGI の場合も SSI の場合も同じだ. 完全に同じ変数が提供されるわけではないが, この種の処理によく使われそうな主要なものについては どちらでも提供されている.

以下に主要なものについての意味をまとめておく.

HTTP_ACCEPT, HTTP_ACCEPT_CHARSET, HTTP_ACCEPT_LANGUAGE

ここに挙げた 3 つの変数は, それぞれ ブラウザが対応している MIME タイプ, ブラウザが解釈できる文字コードと言語という情報を持っている. CGI プログラムではこうした情報を元にして よりブラウザに適した形式のデータを返すといったことが 可能となる*11

なお HTTP_ で始まる変数にはブラウザからの リクエストに付加されてきた情報が収められている. したがって, これらの変数は必ずしも設定されていないこともあるし, 内容を信頼できない場合すらあり得る.

HTTP_USER_AGENT

ブラウザの種類やバージョン情報が入っている.

REMOTE_ADDR

アクセス元の IP アドレス.

REQUEST_URI

リクエストされた URL のホスト名部分より 後の部分の情報がそのまま入っている.

これまで特に説明しなかったが, FORM によらずにリクエストする際の URL に CGI プログラムに対して与える情報をうめ込むことができる. たとえば先の CGI プログラムの例と 同じ情報を CGI プログラムに与えるには http://localhost/cgi_test/result.cgi?name=…&age=… という URL にアクセスする(というのはつまり GET リクエストを送る)ことでも実現できる. 要するに ? を前置いて QUERY_STRING に 入る内容そのものをくっつけてやればよいわけだ.

そして, このようにケースにおける REQUEST_URI/cgi_test/result.cgi?name=…&age=… になり, 次の SCRIPT_NAME との違いがでてくる.

SCRIPT_NAME

リクエストのパス情報が入っている. ここには QUERY_STRING に相当する情報は含まれない.

SCRIPT_FILENAME

リクエストに対応するファイル名のフルパスが入っている.

USER_NAME

IDENT というやり方に基くユーザ名情報が入っている(場合がある)が, このやり方による情報は信頼できないことが多いので 一般には使用できない.

またまた前置きが長くなってしまったが, CGI プログラムの実験をするために CGI のための準備をしてゆくことにしよう.

CGI のための準備

SSI の場合と同じで CGI の環境を作るためには モジュールの組み込みが必要となる.

LoadModule cgi_module /usr/lib/apache/1.3/mod_cgi.so

といった記述を必要に応じて httpd.conf に 加えておこう. SSI のときにそうだったように RPM 系のディストリビテューションにおいては, デフォルトで組み込まれているので, そうする必要はないだろう.

続いてハンドラの設定だ. ハンドラの設定は AddHandler で行うことができる. ハンドラ名は cgi-script である. リクエストに対応するファイルに cgi-handler が割り当てられていると, そのファイは CGI プログムであるとして扱われる. 逆に言えば cgi-handler を 割り当てたファイルはなんであれ CGI プログラムになってしまあということだ.

ところで先程は説明しなかったのだが, ハンドラの割り当ては SetHandler という ディレクティブによっても行うことができる. AddHandler は拡張子を指定して, その拡張子を持つファイルに対して ハンドラを割り当てるものであるが, SetHandlerDirectory の中や .htaccess の中に指定し 対応するディレクトリにある すべてのファイル対して ハンドラを割り当てるという働きを持つ. たとえばこうだ.

<Directory /var/www/cgi_test>
  SetHandler cgi-handler
</Directory>

これによって /var/www/cgi_test 以下にある すべてのファイルに cgi-handler が 割り当てられることになる. ただ, このままでは /var/www/cgi_test/test.html までが CGI プログラムになってしまって具合いが悪いので, ここでは次のようにしよう.

<Directory /var/www/cgi_test/cgi-bin>
  SetHandler cgi-handler
</Directory>

そして CGI プログラムは /var/www/cgi_test/cgi-bin の下に まとめて置くことにする. ちなみに, 先の SetHandler の働きと 同じようことは ScriptAlias によっても可能だ. ただし ScriptAlias の場合は, 次のように, URL とディレクトリの対応関係を 新たに作るという別の働きもある.

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/

最後にやはりこれが必要だ.

Options +ExecCGI

これは SSI のときの +Includes に相当する. さあこれで準備は整った.

CGI プログラムのサンプル

CGI プログラムは, ここまで説明してきたような やり取りの方法をまもってさえいれば どのようなものによっても作ることができる. sh スクリプトでも作ることができるだろうし, C 言語によっても作ることができるだろう. しかしより一般的に使われているのは Perl だろう. 実際 Perl では CGI プログラムを作るのに 便利なモジュールが提供されていたりもする. また筆者としては, これは Ruby でも同様の状況であると 言っておきたい :-)

ともあれ, そいうったわけなので, ここでは Perl と Ruby による簡単なサンプルを示すことにしよう. 前述のサンプル FORM をベースに いくつかの cgi-bin/result.cgi を作ってみる.

まずはこのようなものを考えてみよう. cgi-bin/result.cgi を次の内容としてみたとする.

#!/usr/bin/perl
use CGI qw/:standard/;
use NKF;

$x = new CGI;

# ヘッダの出力
print "Content-Type: text/html; charset=iso-2022-jp\n\n";
# これ以降はボディ部
print "<HTML><HEAD><TITLE>CGI Sample</TITLE></HEAD><BODY><PRE>";

%k = $x->Vars;
foreach (keys(%k)) {
    print nkf('-j', "$_ = $k{$_}\n");
}

print "</PRE></BODY></HTML>";

これは CGI プログラムに与えらた入力値を そのまま表示するだけのものである. 先頭あたりの CGI は CGI プログラムのために作られた Perl 用のパッケージで, これを使うと CGI プログラムを ある程度楽に書くことができる(この例ではあえて それほど「楽」をしないようにしている).

同様のものを Ruby で書くとこのようになる.

#!/usr/bin/ruby
require 'cgi'
require 'nkf'

x = CGI::new()

# ヘッダの出力
print "Content-Type: text/html; charset=iso-2022-jp\n\n"
# これ以降はボディ部
print "<HTML><HEAD><TITLE>CGI Sample</TITLE></HEAD><BODY><PRE>"

x.params.each do |k,v|
  print NKF::nkf('-j', "#{k} = #{v}\n")
end

print "</PRE></BODY></HTML>"

require している cgi ライブラリは やはり CGI プログラム用のクラスライブラリで, CGI プログラムにおいてめんどうな部分――たとえば URL エンコードを解除するなどといった処理をしてくれる.

どちらの場合も nkf ライブラリを用いて 出力を JIS コードにしているが, これはヘッダ部で文字コードを ISO-2022-JP である と宣言しているのに合わせるためである. もちろんこのやり方では 対応できるのはせいぜい日本語だけであるから, 一般的にはあまり良いやり方とは言えない. CGI プログラムではこうした文字コードの扱いが問題となることが多いが, どのように解決すべきかは個々のケースによるもので 一般的な解というのはおそらくないだろう. 言語を限定することである程度問題を簡単にすることはできるが, それでもまだ十分に難しい問題だ.

次の例は先の例を少しだけ発展させたものだ. アンケートなどのような, 入力値を保存しておきたい場合を考えてみる.

#!/usr/bin/ruby -Ke
# このファイルは自体は日本語 EUC で保存する

require 'cgi'
require 'nkf'

DBFILE = '/tmp/db.txt'
AGE = ['〜19', '20〜29', '30〜49', '50〜']

def jis_print(x)
  print NKF::nkf('-j', x)
end

x = CGI::new()

print "Content-Type: text/html; charset=iso-2022-jp\n\n"
print "<HTML><HEAD><TITLE>CGI Sample</TITLE></HEAD><BODY><PRE>"

name = NKF::nkf('-e', x.params['name'].join())
age  = NKF::nkf('-e', x.params['age'].join())

if name.size == 0                       # 入力チェック(1)
  jis_print "名前を入力してください\n"

elsif age.size == 0 ||                  # 入力チェック(2)
    ! AGE.include?(age)
  jis_print "年齢を選択してください\n"

else                                    # 入力値は正常
  begin
    db = open(DBFILE, 'a')              # ファイルをオープン
    begin
      db.print "#{name}: #{age}\n"      # ファイルに書き込む
    rescue                              # システム上のエラーが起きた場合
      jis_print "データベースに書き込めません\n"
    end

  rescue                                # システム上のエラーが起きた場合
    jis_print "データベースファイルをオープンできません\n"
  ensure
    db.close                            # ファイルをクローズ
  end
  jis_print "#{name}: #{age}\n"         # ブラウザへの出力
end

print "</PRE></BODY></HTML>"

簡単な入力内容のチェックをしているので少し長くなったが, 実質的に加ったのはファイルをオープンしてそれに書き込み, そして閉じるという操作だけだ.

ここでいくつか注意したいことがある. まず一つは, プログラムでどのような処理をするにしろ, あるいは処理できなかったにしろ, なんらかのレスポンスをブラウザに返してやる必要があるということだ. リクエストとレスポンスは一対になっているので, これは必須である. もしも CGI プログラムが何も出力しないと Internal Server Error という状況になり ブラウザにもそのようなメッセージが表示される. これを見せられたユーザは途方に暮れるしかないだろう.

また, 一般に CGI プログラムは 並行して複数が動作し得るということにも注意すべきである. たとえば上のサンプルプログラムであるが, これはそのようなことを考えて作られてはいない. よって, このプログラムがほぼ同時に起動されると 困ったことが起る. それは何かというと データベースファイルの内容が 壊れるという致命的なことである. このことを避けるにはしかるべきタイミングで 排他処理を行うことが重要となる.

排他処理とは単純にはファイルのロック処理になるが, さいわい Ruby にはこのようなケースにすぐに使える 簡易データベース PStore が添付されている. PStore を使って, 先のプログラムの後半を修正すると, たとえばこのようになる.

begin
  db = PStore.new(DBFILE)             # データベースをオープン
  begin
    db.transaction do                 # トランザクション開始
      db[name] = age
    end                               # トランザグション終了
  rescue                              # システム上のエラーが起きた場合
    jis_print "データベースに書き込めません\n"
  end

rescue                                # システム上のエラーが起きた場合
  jis_print "データベースファイルをオープンできません\n"
end

jis_print "#{name}: #{age}\n"         # ブラウザへの出力

この中の「トランザクション開始〜終了」の部分で データベースファイルがロックされている. したがってデータベースへのアクセスを この内に限れば(実はその外ではできないのだが) 安全にデータを変更できるということになる.

セキュリティに関する注意

最後に CGI に関するまとめにかえて, セキュリティに関する注意事項に触れておこう.

SSI の節でも述べたことだが, CGI プログラムは一般に Apache サーバが 動作している権限で起動される. つまり httpd.confUser および Group ディレクティブに設定された ユーザ/グループの権限である. したがって, たとえば CGI プログラムを 自由に設置して使えるようにしている環境などでは, 思いもよらぬ情報が CGI プログラムを介して 外部にもらされてしまうということがないわけではない. この点には注意が必要であろう.

また, このことに関連して ファイルへの書き込みを行うような CGI プログラムについての 注意事項を導くことができる. つまり, ファイルを作る際に Apache サーバの 権限で書き込める必要があるということだ. 前節の例ではなにげなく通りすぎてきたが, その背景にはこのような問題があったことをここで追記しておく. この問題についての一つのやり方は, そうすることが可能ならだが, ファイルが作られるディレクトリの グループを Apache サーバのそれと同じくしておいて モードを 770 にしておくといったものであろう. 所属グループの関係などによって そうする事ができない場合には, ディレクトリのモードを 707 や 777 にすることで 回避できる場合もあるが, これはできるかぎり避けたいやり方である.

以上とは別の問題として, ブラウザからの入力値は本質的に信用できないという問題がある. 悪意ある入力によれば, どのような入力値でも与えることができるから, たとえ入力 FORM において選択枝になっている値でも そのチェックを怠ることはできない. また CGI プログラムの中から, 入力値を与える形で別コマンドを実行するような 場合には特に注意が必要で, 入力値を念入りにチェックしなくてはならないだろう.

Part 4: 目的別カスタマイズ

ここからはケーススタディという形で, 目的にそった運用のやり方をいくつかの場合について見てゆきたい. よくあるパターンというような例を選んだつもりなので, 実際の運用のときに参考にしてほしい.

一つのマシンで複数サイトを構築する

一つのマシンにいくつものホスト名を付けることは, そのように DNS の設定をしてやることで可能である. また, 設定した各名前に応じて 違ったページを見せてやるということも Apache サーバの設定次第で可能となる. そうして一つマシン内に複数サイトを構築し, 実際にはない複数のマシンがそこにあるかのように 見せるというテクニックのなかで, その各仮想的なマシンのことを 指してバーチャルホストと呼んでいる.

バーチャルホストの設定のキーワードは VirtualHost だ.

複数の IP アドレスを使って構築するバーチャルホスト

VirtualHost ディレクティブは次のようにして使う.

<VirtualHost 10.0.0.1>
 ServerName virtual1.domain.name
 DocumentRoot /var/www/virtual1
</VirtualHost>

<VirtualHost 10.0.0.2>
 ServerName virtual2.domain.name
 DocumentRoot /var/www/virtual2
</VirtualHost>

VirtualHost の引き数として設定されている 10.0.0.110.0.0.2 は バーチャルホストとしてリクエストを 受け付ける IP アドレスである. <VritualHost></VirtualHost> で くくられた部分の記述は, そのバーチャルホストに対してたみ有効な設定となる. ここでは DocumentRoot を バーチャルホスト毎に違ったもたに設定しているが, これはつまり, このマシンには複数の IP アドレスが割り当ててあって, その IP アドレス毎に違ったページを見せようとしているということになる.

このようなケースでは, おそらく運用時には 10.0.0.1virtual1.domain.name という名前で, 10.0.0.2virtual2.domain.name という名前で それぞれ参照できるようにすることになるであろう. しかし, この設定のやり方においては, 実際には, 名前はそれほど本質的なものではない *12. あくまでリクエストが届いた IP アドレス によって 処理を切り分けているからだ. よってこのやり方のバーチャルホストを IP アドレスベースのバーチャルホストと呼ぶ.

一つの IP アドレスで構築するバーチャルホスト

前述したのとは 少し違うアプローチに名前ベースのバーチャルホストというのがある. この場合の設定はこうだ.

NameVirtualHost 10.0.0.1

<VirtualHost 10.0.0.1>
 ServerName virtual1.domain.name
 DocumentRoot /var/www/virtual1
</VirtualHost>

<VirtualHost 10.0.0.1>
 ServerName virtual2.domain.name
 DocumentRoot /var/www/virtual2
</VirtualHost>

NameVirtualHost が加わり, さらに 2 つのバーチャルホストの設定は どちらも 10.0.0.1 に対するものになった.

名前ベースのバーチャルホストでは IP アドレスが異っていなくてもかまわない. 同じでよい. というのは, こちらのやり方においては処理の切り分けを ブラウザからのリクエストの中に含まれている アクセス先ホスト名を元に行っているのである (コラム参照). この設定例について言えば, virtula1.domain.name に対するリクエストは 最初の VirtualHost 部分の設定に従って処理され, virtual2.domain.name に対するものは 後の VirtualHost 部分の設定に従って処理される.

いずれのやり方を採るにせよ VirtualHost を活用すれば, たった一つの Apache サーバを動かしているだけにもかかわらず, 相互に独立した複数の Web サイトを運用することが可能となる. VirtualHost の中に書ける, つまり 各バーチャルホスト毎に設定を違えることのできる ディレクティブは多数あって, マニュアルの Context 欄に virtual host と書かれているディレクティブがそうだ. これによればログファイルも前述の SSI や CGI に関係するどの設定も, バーチャルホスト毎に設定できることがわかるだろう.

さて, バーチャルホストの設定に関して 注意しなくてはならいことにこのようなことがある. それはデフォルトの設定がどうなるのかということだ. ここに IP アドレスを一つだけもったマシンがあったとする. このマシンの IP アドレスは 10.0.0.10 であり, これに対する名前はたくさんある. x.domain.name, y.domain.name, z.domain.name…. そしてこのマシンの上で動いている Apache サーバでは 次のような設定がなされていたとしよう.

DocumentRoot /var/www/Z

NameVirtualHost 10.0.0.10
<VirtualHost 10.0.0.10>
  SeverName x.domain.name
  DocumentRoot /var/www/x
  …
</VirtualHost>
<VirtualHost 10.0.0.10>
  SeverName y.domain.name
  DocumentRoot /var/www/y
  …
</VirtualHost>

VirtualHost に関する設定はこれだけだとすると, z.domain.name に対するリクエストが届いたときに Apache サーバはどのようにふるまうのであろうか. その場合の DocumentRoot/var/www/Z ではなく /var/www/x になる. つまり x.domain.name に対する設定が有効になってしまうわけである. そのような場合にどの設定が活きてくるかについては 設定内容の検証の際に紹介した方法で確認することができる.

$ /usr/sbin/apache -S
VirtualHost configuration:
10.0.0.10:80           is a NameVirtualHost
                       default server x.domain.name (/etc/apache/httpd.conf:269)
                       port 80 namevhost x.domain.name (/etc/apache/httpd.conf:269)
                       port 80 namevhost y.domain.name (/etc/apache/httpd.conf:272)

この中で default server となっているのが デフォルトのバーチャルホストであるというわけだ.

アクションの設定

SSI や CGI の節では, ファイルに対して ハンドラというものを割り当てることができると解説した. つまり自分でハンドラを作り 適宜ファイルにそれを割り当ててやれば 任意の処理を通して情報を送り出すことができるわけなのだが, これには荷がかちすぎているという人も少なくないだろう.

そんなときには action_module を使ってみてはどうだろうか.

action_module を使う前に モジュールが組み込まれていることを確認しよう. -l オプションを付けて Apache サーバを起動すれば 静的に組み込まれているモジュールを確認できるし, 動的に組み込むなら LoadModule だ.

LoadModule action_module /usr/lib/apache/1.3/mod_actions.so

そして次のような設定を行う.

<Directory /var/www/action_test>
 AddType application/x-httpd-eruby .rhtml
 Action application/x-httpd-eruby /cgi_test/eruby
</Directory>

Directry によって /var/www/action_test 以下に 効力を限定した上で, AddType によって .rhtml という拡張子のファイル対して application/x-httpd-eruby という MIME タイプを設定する――というところまでは 理解できるはずだ. その次からがここでのポイントである.

Action は指定された MIME タイプをアクションを設定する. この例で何が起こるかというと, MIME タイプが application/x-httpd-eruby であると設定されている ファイルへのリクエストが届くと, Apache サーバは /cgi_test/eruby なる CGI プログラムを起動してその出力をもってレスポンストとするのである. この際, 元々のリクエストにあったパス部分の情報が PATH_INFO という環境変数に入れらて, また, 元々のリクエストに対応するファイルが PATH_TRANSLATED という環境変数に入れられて, その CGI プログラム /cgi_test/eruby が起動される.

この例に登場している eruby というプログラムは これら二つ変数を参照した上で, 元々のファイルの内容を 適当に解釈し出力するという動作をしているわけだ.

ユーザのホームページを公開する

サーバの利用者ためののホームページとしては http://www.domain.name/~foo/ という URL が使われ, それにはユーザ foo のホームディレクトリの中の public_html が対応していることが比較的多い. このとき URL の中の /~foo/ と サーバのディスクの中の ~foo/public_html を 結びつけているのは userdir_module モジュールである. 多くの場合, このモジュールはなんらかの形で 組み込まれているだろうが, これを外すと /~foo/ を参照できなくなるのがわかるだろう.

/~foo/ で見られるディレクトリを調整する

ところで userdir_module では ユーザのホームページとして公開する ディレクトリを任意に設定することができるようになっている. そのためのディレクティブが UserDir である. UserDir のデフォルトは public_html となっており, これは上記のような /~foo/~foo/public_html というマップを意味している. これは各ユーザのホームディレクトリに対する 相対パスであると考えればよいだろう.

また UserDir/ からはじまるディレクトリ名を記述すると, ユーザのホームディレクトリを基準にするのではなく 指定されたディレクトリを基準にして, それにユーザ名を付けたものが /~foo/ にマップされるようになる. さらにこの場合には, ディレクトリ名の中に 一つだけ * を指定することができる. ディレクトリ名の中に * があると, リクエストをファルにマップしようとする際に Apache サーバはその部分をユーザ名で置き換える.

ところで, どこかのページを見ようとしたら 別のページに飛ばされたという経験はないだろうか. そのようなふるまいをブラウザに強要することを リダイレクトというのだが, このリダイレクトという手法を使う形で このようなことをすることも可能である.

なお, UserDir の Context には virtual host が含まれているため, 後者のようにフルパスによる設定をしておくと バーチャルホスト毎にユーザのホームページを 変えてやることも可能である(明示的に設定しない限り そのようにはならないことに注意).

ユーザのホームページを http://foo.domain.name/ にする

ここで少し変った形でユーザのホームページを 設定することを考えてみよう. ユーザのホームページを http://www.domain.name/~foo/ のような URL としてではなく http://foo.domain.name/ として見せることはできないだろうか.

これはいくつかのやり方によって実現することができる.

一つの方法は vhost_alias_module による VirtualDocumentRoot を使うというものだ. VirtualDocumentRoot は リクエストにあったアクセス先ホスト名を . を区切りに分解し, その中の任意の部分を取り出して 動的に DocumentRoot を設定するというやや複雑な挙動を示す. 少しわかりにくいかもしれないが, ここでの目的を達成するには こういう設定をしてやればよい.

UseCanonicalName off
VirtualDocumentRoot /org/%2/%1

%n によってホスト名の n 番目の部分を参照でき, %-n によって後から数えて n 番目の部分を参照できる. たとえば上の例の設定をした場合, http://www.domain.name/ に対する DocumentRoot/org/domain/www になる.

www.domain.name
--- ------ ----
%1    %2    %3
--- ------ ----
%-3  %-2   %-1

同様に http://foo.domain.name/ の場合には /org/domain/foo になるから, このエリアをユーザ foo に 開放してやればよいということだ. なお, ここで UseCanonicalName という見なれないディレクティブが 登場しているが, これを off にすると リクエストに付加されていたアクセス先ホスト情報を 強制的に ServerName ディレクティブで 設定された値に書き換えるという機能を抑制することができる. ここで行っているようなやり方においては, そのような機能が働いていると すべてのリクエストについての正しいホスト名情報を 得られなくなってしまうため, あえて off にしているわけだ.

ただしこの方法には若干クセがあるようで, 他の方法によるバーチャルホストの設定とうまく 共存できないようなのである. したがって, マップするディレクトリの配置をある程度考えなておかないと 全体として破綻してしまう可能性もある. そこで, もう一つのより柔軟性が高く, かつ設定が複数な方法が別にあるということを 紹介しておこう.

rewrite_module を使ったバーチャルホスト

まずは次のサンプルを見てほしい.

UseCanonicalName off

RewriteEngine on
RewriteMap lowercase int:tolower

RewriteCond ${lowercase:%{SERVER_NAME}} !^rb\.domain\.name$
RewriteCond ${lowercase:%{SERVER_NAME}}  ^[a-z][-_a-z0-9]+\.domain\.name$
RewriteRule ^(.+) ${lowercase:%{SERVER_NAME}}$1  [C]
RewriteRule ^([a-z][-_a-z0-9]+)\.domain\.name/(.*) /home/$1/public_html/$2

UseCanonicalNameoff しているのは先と同じ理由による. rewrite_module の設定においては %{SERVER_NAME} という記述によって リクエストに付いてきたホスト名情報を参照できるのだが, このときに UseCanonicalNameon になっていると ここで考えているような動作をさせることができない. そのため, ここでもあえてこれを off にしているのである.

さて本題である. まず RewriteEngine. これは rewrite_module の機能を有効にするかどうかを設定している. デフォルトは off であるから, まずはこれを on にしなくては話が始まらない. 続く RewriteMap は後で参照されている マップと呼ばれる変換テーブルを定義するためのものだ. 通常, マップの実体としては数種類の形式(テキストや dbm, コマンド実行など)の データベースのいずれかが指定され, その中にキーとそれに対応する値の組を収めておく. しかし, ここでは int という, いわば疑似データベースを指定し, 与えられたキーを小文字に変換するという 動作をするマップ lowercase を定義している. なお lowercase は後に続くルールの中で ${lowercase:%{SERVER_NAME}} という形で参照されている. これは lowercase%{SERVER_NAME} なる文字列 (前述の通りこれはアクセス先ホスト名を表している)を与え, それに含まれる英大文字を小文字に変換する.

次に出てくるのは RewriteCond である. 2 つあるこれらの記述は, その後に続くルールを実行するかどうかの条件を設定するものである. RewriteCond の書き方はこうだ.

RewriteCond  対象とする文字列  マッチを試みる正規表現

対象とする文字列マッチを試みる正規表現に うまくマッチすれば続く部分に進み, マッチしなければ そこでそのリクエストについてはあきらめる. rewrite_module の目的というのは, リクエストされた URL を実際のファイルに対応付けたり, 別の URL へリダイレクトすることである. ここであきらめると言ったのは, そうした処理をあきらめて通常の処理に切り換える ということを意味している.

具体的な説明はひとまずおいておいて先に 続くルールの方を見てみよう. ルールというのは RewriteRule の部分で, ここで実質的な処理を行う. 書式はこうだ.

RewriteRule  マッチを試みる正規表現  書き換えパターン  [フラグ]

RewriteRule にはリクエストされた URL のパスの部分が渡され, それ対して マッチを試みる正規表現 が マッチするかどうかをチェックする. もしもマッチしたら 指定された 書き換えパターン によって パス部分を書き換えてゆく. これはちょうど Perl の s/正規表現/書き換えパターン/ に似た動きをする. マッチを試みる正規表現 の中の (…) でくくられた部分を 書き換えパターン の中で $n のような形で参照できるのも同じである.

$path =~ s/正規表現/書き換えパターン/;

複数の RewriteRule が並んでいるときには, 基本的には以下の Perl のコードとの挙動と同じ動きをする.

$path =~ s/正規表現1/書き換えパターン1/;
$path =~ s/正規表現2/書き換えパターン2/;
                 :
                 :

つまり, 各ルールにさらのパス名が渡されるのではなく, 直前までの書き換えによって適宜変形されてきた 元々はパス名であったものが順次渡されてゆくわけだ (Perl のたとえで 1 行目も 2 行目も同じ $path を 対象としているのはそういう意味である). ただし, このあたりの挙動は与えられたフラグによって変化する. フラグが与えられない場合は上記の通りだが, 先のサンプルのように C フラグが指定してあると違ってくる.

それではサンプルの一連の記述が, 実際にはどのように働くのかを説明しよう.

RewriteCond ${lowercase:%{SERVER_NAME}} !^www\.domain\.name$

%{SERVER_NAME} はリクエスト先のホスト名である. そしてそれが lowercase マップに渡されているのであるから, ここでの文字列はすべて小文字で表現したホスト名, たとえば foo.domain.name になる. 通常はこれに対して正規表現のマッチを試みるのであるが, このケースでは正規表現が ! ではじまっているため ^www\.domain\.name$ という正規表現にマッチしなければ 続く部分を実行する, と, こいういう意味になる.

よって foo.domain.name^www\.domain\.name$ に マッチしないから, このチェックをパスするが, これが www.domain.name であると ^www\.domain\.name$ にマッチするため このチェックを通り抜けられない.

ここでは foo.domain.name に対するリクエストがあったことにして 次に進んでみよう.

RewriteCond ${lowercase:%{SERVER_NAME}}  ^[a-z][-_a-z0-9]+\.domain\.name$

このチェックの正規表現には ! がついていないから, 先とはちがってマッチしたら次に進む, だ. 対象となる文字列は先程と同じく 小文字ホスト名であるから foo.domain.name. 正規表現は ^[a-z][-_a-z0-9]+\.domain\.name$ であるから, これにマッチする.

したがってこの後に続く RewriteRule が 実行されることになる.

RewriteRule ^(.+) ${lowercase:%{SERVER_NAME}}$1  [C]

このルールの正規表現は ^(.+) であるから パス部分がなんであってもマッチする. ここではそれが /diary/ であったとしよう. /diary/^(.+) にマッチし, しかも ( ) でくくってあるから, 後で $1 によって参照できる. 書き換えパターンは ${lowercase:%{SERVER_NAME}}$1 であるから, 最初与えられたパス /diary/ は, このルールによって foo.domain.name/diary/ に書き換わる. Perl で表現するとこのようになるだろう.

# 本来はリクエストによって設定される
$SERVER_NAME = 'foo.Domain.Name';
$path = '/diary/';

# lowercase マップの働き
$server_name = $SERVER_NAME;
$server_name = tr/A-Z/a-z/;

# このルールの動作
$path =~ s!^(.+)!$server_name$1!;

書き換え後の文字列は, 続くルールへの入力となる.

RewriteRule ^([a-z][-_a-z0-9]+)\.domain\.name/(.*) /home/$1/public_html/$2

このルールに対すして与えられるのは 先程の foo.domain.name/diary/ だ. この文字列は正規表現 ^([a-z][-_a-z0-9]+)\.domain\.name/(.*) にマッチするので ここでもまた書き換えが行われる. 次の書き換えはこのようになる.

$path =~ s!^([a-z][-_a-z0-9]+)\.domain\.name/(.*)!/home/$1/public_html/$2!;

結果は /home/foo/public_html/diary/ だ. つまり http://foo.domain.name/ にアクセスすると ユーザ foo のホームディレクトリの中の public_html 以下を参照できるということで, この話の元々の目的を達成できたことになる.

ただし, ここで一つ注意が必要だ. というのは, 前述したように 一つ目のルールには C フラグが指定されている. このフラグの意味するとこは, そのルールと続く次のルールを連結して 一続きのルールとするということだ. C フラグによって連結されたルールは, 前のルールでマッチが成功し書き換えが行われないかぎり 単に無視される. つまりこうだ.

if ($path =~ s!^(.+)!$server_name$1!) {
  $path =~ s!^([a-z][-_a-z0-9]+)\.domain\.name/(.*)!/home/$1/public_html/$2!;
}

もしもさらにルールが連なっていても同様である.

RewriteRule pattern1 replace1 [C]
RewriteRule pattern2 replace2 [C]
RewriteRule pattern3 replace3

           ↓

if ($path =~ s/pattern1/replace1/) {
  if ($path =~ s/pattern2/replace2/) {
    $path =~ s/pattern3/replace3/;
  }
}

なお, ルールに L フラグが指定されていない限り, マッチするしないに関らず次々とルールが試されるという点に 注意しておこう. マッチしたらそこで終りとはならない.

ユーザによってホームページを禁止する

最新に特定のユーザのホームページを 禁止する方法について触れておこう.

UserDir ディレクティブには パス名以外に enabled/disabled という キーワードを指定することができる.

UserDir disabled

このように disabled とだけ書いておくと /~foo をユーザのディレクトリに対応付けるという 処理そのものを行わなくなる. ただし, 次のような形で明示的に許可を与えると, そのユーザに限って /~foo の形のページを持つことができる.

UserDir enabled foo bar ...

ただしこのことも次の形で明示的に 禁止されていない場合に限られる.

UserDir disabled bar

以上 3 つの記述があったとすると, 結果的に /~bar という形で ユーザ bar のページを外部から参照することはできなくなる. UserDir でのパスの設定によっては あらぬとろが外から丸見えということにもなりかねないから, このような形での調整が必要となることもあるだろう.

ブラウザに合わせて情報を提供する

w3m や lynx のようなテキストベースのブラウザは, それ自身では画像を再生することができない. しかし, どうしても伝えたい情報があるので 画像イメージそのもののと, テキストベースのブラウザ用に 文字の組み合わせで図を書いた いわゆる ASCII アートを用意したとしよう. そしてこんな形でリンクを張った.

<A HREF="picture.png">図 1</A>
(テキスト版の<A HREF="picture.txt">図 1</A>)

通常 Apache サーバは拡張子 png は画像ファイルであり txt は単なるテキストファイルであると知っているから, 各自の環境に合わせてリンクをたどってもらえれば それをそのまま見ることができるだろう. また SSI や CGI といった仕組みを活用することで, サーバ側で自動的に対処するというやり方もあるだろう. しかし, それとは別の方法もある.

コンテントネゴシエーションというやり方は その動作が複雑であるわりに設定は簡単だ. 必要に応じて negotiation_module を組み込み, そうして必要なエリアに対して MultiViews オプションを付けてやるだけでよい.

LoadModule negotiation_module /usr/lib/apache/1.3/mod_negotiation.so

<Directory /var/www/negotiation_test>
 Options +MultiViews
</Directory>

そして先程のリンクをこのように直す.

<A HREF="picture">図 1</A>

これではリンクをたどれないではないかと思われるかもしれないが, 実際にはこのリンクをたどることができる. しかもブラウザの機能に合わせて picture.pngpicture.txt のどちらかが表示されているはずだ.

この機能は言語に対しても効有である. たとえば内容が英語で書かれている hello.html.en と 同じ内容を日本語で書いた hello.html.ja を用意する. これらを MultiViews が効いているエリアに置いてやり, ブラウザで hello.html にアクセスするとどうだろう. きちんとブラウザの設定がなされていれば それなりの言語が選択されたはずである*13.

からくりはこうだ. ブラウザはリクエストを出すときに様々な情報を付加している. その中にはそのブラウザが処理できる データの形式や言語といった情報も含まれている. 実は CGI 変数 の節で紹介した HTTP_ACCEPT などの環境変数は そうした情報に基づくものであるのだが, これを見てやることで各ブラウザに適した 内容のレスポンスを返すことが可能となるのである. このような挙動のことをコンテントネゴシエーションと呼んでいる.

すでに述べたように, Apache では, あるルールにしたがった拡張子をつけることで コンテントネゴシエーションに関する調整を できるようにしているわけである. このやり方の一つの応用として次のようなものを挙げておこう. これはコンテントネゴシエーションの本質ではないが, たしかに便利に働くこともある設定だ.

<Location />
  Options +MultiViews
  AddCharset ISO-2022-JP .jis
  AddCharset EUC-JP .euc
  AddCharset SHIFT_JIS .sjis
</Location>

AddCharset によって各拡張子を持つファイルには 対応する文字コード情報が付加される. ここに MultiViews がなければ, それぞれにアクセスすれば正しい文字コード情報が 付加されると言って終るわけだが, MultiViews がセットされていることによって sample.html.jis へは sample.html として アクセスできるようになる. しかも正しい文字コード情報が付いたままでだ. もちろん, こうしたことは MultiViews にたよるまでもなく .htaccess に適切な記述をすることによっても対応できる. しかし一つのディレクトリ配下に 様々な言語・文字コードでか書れたファイルが 混在しているようなケースではどうだろう. こうしたやり方にも検討してみる価値があるはずだ.


*1あえて同じ情報を参照できるようにするケースもあるが, それはあくまで「あえて」そうしてあるためである.
*2これはただのサンプルであり, 実在するものではない.
*3実際には HTTP のバージョンによって動きが少し異ってくる. つまり HTTP バージョン 1.0 に基づくやり取りでは 一つの部品を得る毎に接続を切り, そして再び接続し直すわけだが, HTTP バージョン 1.1 ではそのようなことをしない.
*4<URL:http://www.netcraft.com/survey/> など.
*5Linux Conference 2000 Spring にて来日. ASF の現在の President.
*6<URL:http://www.opensource.org/osd.html> で参照できる. 日本語訳は <URL:http://www.geocities.co.jp/SiliconValley-PaloAlto/9803/osd-ja/osd-ja.html> から.
*7ちなみに現在の開発バージョンである woody には Apache 1.3.12 をベースとしたパッケージがある.
*8こういう作業を バックポートと呼ぶことがある
*9コラムの例にある レスポンスヘッダの中の Content-Type というのが それに関係する部分だ. この Content-Type の中の charset が iso-8859-1 になる.
*10動的組み込み機能については Apache サーバを構築する際に明示的にその機能を 使えるよう指示しておく必要がある. なお今回紹介した各ディストリビューションの バイナリパッケージに関しては, いずれもこの機能を有効にした Apache サーバを 提供している.
*11そのようなことを CGI プログラムによってではなく Apache サーバの機構として実現しているのが 後述の MultiViews である.
*12しかしでたらめでも良いというようなものでもない. Apache サーバ自身がいくつかのケースで 自サイトへのリンクを作る際に ServerName に指定された ホスト名を使うからである.
*13httpd.confAddLanguage ja .ja といった記述がなければ それを加える必要がある.