この記事の対象読者
- そもそも「Apache」ってなに?の人。
- 「ApacheにURLを書き換える機能がある」ことは知っているが、どう書けばいいのか分からない人
- 設定ファイルの断片だけ見せられても、それがファイル全体のどこに書かれるのかイメージできない人
- 「Cloudflareがあるなら、Apacheっていらなくない?」と思ったことがある人
SQL・プログラミングの知識は前提にしません。Apacheの設定ファイルを一度も開いたことがなくても読めるよう記載しました。
1. そもそもApacheは何をしているのか
まず全体像から。ブラウザでURLを開いたとき、リクエストは次のような経路をたどります。
Apacheは ブラウザとアプリの間に立つ「受付係」 です。受付係なので、アプリにリクエストを渡す前にいろいろな判断ができます。
- このURLは静的ファイル(画像など)だから自分で返そう
- このURLはアプリに転送しよう
- このURLは古いから、新しいURLに書き換えてから処理しよう ← これがリライト
- httpで来たから、httpsに行き直してもらおう ← これもリライト機能でやることが多い
この「URLの書き換え・行き先変更」を担当するのが mod_rewrite というApacheのモジュールです。
2. 「CloudflareみたいなCDNがあるなら、Apacheは不要では?」
最近のWeb構成を知っていると、ここで疑問が湧きます。「CDNもキャッシュしたりリダイレクトしたりするよね? Apacheと何が違うの?」
答えは 「置き換え」ではなく「層が違う」 です。
ブラウザ
│
▼
Cloudflare(CDN)─── 世界中に分散した「他人の」エッジサーバー
│ ・キャッシュできるもの(画像・JS・CSS等)はここで返して終わり
│ ・DDoS防御・WAFもここ
│
▼ キャッシュにないもの・動的なものだけが通過してくる
オリジンサーバー ─── 自分が管理している「自分の」サーバー
│
Apache / Nginx ── オリジン側の「受付係」
│ ・リバースプロキシとしてアプリに転送
│ ・リライト・バーチャルホスト・アクセス制御
│
▼
Webアプリ(Spring Boot / PHP / Node...)
ポイントは、CDNは「自分のサーバーの手前にある他人の分散ネットワーク」であって、自分のサーバーの中に入ってきてくれるわけではないこと。CDNのキャッシュを素通りしたリクエスト(ログイン処理、DBを見ないと返せないページ)は最終的にオリジンに届くので、オリジン側でHTTPを受けてアプリに割り振る係——それがApacheやNginx——は依然として必要です。
ただし、Apacheの仕事の一部は実際にCDNへ移せます。
| Apacheの機能 | CDNに移せる? |
|---|---|
| http→https強制、www統一、301リダイレクト | 移せる(CloudflareのRedirect Rules等。エッジで返すので速い) |
| 静的ファイル配信 | ほぼ移る(CDNキャッシュの主目的そのもの) |
| きれいなURLの内部リライト(後述) | 移せない。アプリの処理先の差し替えなので、アプリと同じ場所でやる必要がある |
| リバースプロキシ(アプリへの転送) | 移せない。オリジン内部の交通整理はオリジンの仕事 |
まとめ: 「CDNはエッジでのキャッシュと防御、Webサーバーはオリジンでのアプリへの振り分けで、層が違うので共存します。リダイレクトや静的配信など一部の役割はCDN側に寄せられます」
3. 「リダイレクト」と「リライト」の違い【最重要】
mod_rewriteを理解する上で一番大事な区別がこれです。どちらもURLを変えるのですが、ブラウザに知らせるかどうかが違います。
リダイレクト(外部転送)
- ブラウザに「引っ越しました」と返事する
- アドレスバーのURLが変わる
- 通信が2往復発生する
リライト(内部転送)
- Apacheの内部だけで処理先を差し替える
- ブラウザは何も気づかない。アドレスバーもそのまま
- 通信は1往復のまま
| リダイレクト | リライト(内部転送) | |
|---|---|---|
| アドレスバー | 変わる | 変わらない |
| 通信回数 | 2回 | 1回 |
| 主な用途 | サイト移転・https強制 | きれいなURLの実現 |
「ブラウザにURL変更を通知して再リクエストさせるのがリダイレクト、サーバー内部で処理先だけ差し替えてブラウザには見せないのがリライト」
4. 設定ファイルの全体像 — リライトは「どこに」書くのか
断片のコードだけ見ても「で、これをどこに書くの?」となるので、実物に近い形の設定ファイル全体を見てみます。
Apacheの設定は、典型的には次の2段構えです。
/etc/httpd/
├── conf/
│ └── httpd.conf ← 本体。サーバー全体の設定(滅多に触らない)
└── conf.d/
├── example.com.conf ← サイトごとの設定(普段触るのはこっち)
└── another-site.conf
① httpd.conf(本体)— サーバー全体の骨格
# ============================================================
# /etc/httpd/conf/httpd.conf
# サーバー全体の設定。説明に必要な部分だけ残した簡略版
# ============================================================
ServerRoot "/etc/httpd" # Apache関連ファイルの置き場所
Listen 80 # 80番ポート(http)で待ち受け
Listen 443 # 443番ポート(https)で待ち受け
# --- 使う機能(モジュール)の読み込み ---
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule rewrite_module modules/mod_rewrite.so # ★リライト機能はこの1行で有効化
LoadModule ssl_module modules/mod_ssl.so # https用
LoadModule proxy_module modules/mod_proxy.so # リバースプロキシ用
LoadModule proxy_http_module modules/mod_proxy_http.so
User apache # Apacheプロセスを動かすOSユーザー
Group apache
ServerAdmin admin@example.com
ErrorLog "logs/error_log" # サーバー全体のエラーログ
LogLevel warn
# デフォルトは「全部拒否」にしておき、サイトごとに必要な場所だけ許可する
<Directory />
AllowOverride none # .htaccess はデフォルト無効
Require all denied
</Directory>
# --- サイトごとの設定ファイルを読み込む ---
IncludeOptional conf.d/*.conf
httpd.conf の役割は「ポート・モジュール・安全なデフォルト」を決めて、サイト個別の設定は conf.d/ に丸投げすることです。リライトを書くのは次のサイト別ファイルの方です。
② example.com.conf(サイト別)— リライトはここに書く
# ============================================================
# /etc/httpd/conf.d/example.com.conf
# example.com というサイト1つ分の設定
# ============================================================
# ---- http(80番)で来たアクセス:httpsへ誘導するだけの入り口 ----
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
RewriteEngine On
# 何が来ても同じパスのhttpsへ301リダイレクト
RewriteRule ^(.*)$ https://example.com$1 [R=301,L]
</VirtualHost>
# ---- https(443番):本命。サイトの実体はこちら ----
<VirtualHost *:443>
ServerName example.com
DocumentRoot "/var/www/example" # 静的ファイル(html,画像等)の置き場所
# SSL証明書
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/example.com.crt
SSLCertificateKeyFile /etc/pki/tls/private/example.com.key
# ログはサイト単位で分ける
ErrorLog "logs/example.com_error_log"
CustomLog "logs/example.com_access_log" combined
# DocumentRoot配下だけアクセス許可(httpd.confの「全部拒否」を上書き)
<Directory "/var/www/example">
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# ============ ここからリライト設定 ============
RewriteEngine On
# (a) www付きで来たら、wwwなしへ統一(301リダイレクト)
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com$1 [R=301,L]
# (b) 旧ブログURLの移転。新旧に法則があるので1行で全記事に対応
RewriteRule ^/old-blog/(.*)$ /blog/$1 [R=301,L]
# (c) きれいなURL → アプリの実体へ「内部転送」(ブラウザには見えない)
RewriteRule ^/products/([0-9]+)$ /index.php?id=$1 [L,QSA]
# (d) /api/ 以下は背後のアプリサーバーへ転送(リバースプロキシ)
ProxyPass /api/ http://127.0.0.1:8080/api/
ProxyPassReverse /api/ http://127.0.0.1:8080/api/
</VirtualHost>
この1ファイルで、リライトが設定全体のどこに住んでいるかが見えたと思います。ポイント:
-
<VirtualHost>は「サイト1つ分」の箱。1台のApacheで複数サイトを運用するための仕組みで、リライトは基本この箱の中に書く - 80番の箱は「httpsへ追い返すだけ」、443番の箱が本命——という2箱構成が定番
- リライト(b, c)とリバースプロキシ(d)が同じ箱に同居している。これがまさに「受付係」の仕事一式
設定を変えたら反映には再読み込みが必要です。
apachectl configtest # まず文法チェック(Syntax OK と出ればよし)
systemctl reload httpd # 設定の再読み込み(接続を切らずに反映)
③ .htaccess — 管理者権限がないときの代替手段
もう1つの記載場所として .htaccess があります。公開ディレクトリに置くファイルで、サーバー再起動なしで即反映されます。
| 場所 | 誰が使う | 反映タイミング |
|---|---|---|
httpd.conf / conf.d/*.conf
|
サーバー管理者 | 再読み込み(reload)時 |
.htaccess |
管理者権限がない人(レンタルサーバー利用者など) | 即時(リクエストのたびに読まれる) |
.htaccess は手軽ですが、毎リクエスト読み込まれるぶん性能が少し落ちます。自分でサーバーを管理できるなら conf ファイルに書くのが定石です(上のhttpd.confで AllowOverride none にしていたのは「.htaccessは使わない」宣言です。使いたいディレクトリだけ AllowOverride All にします)。
書く場所で正規表現の先頭が変わる罠: <VirtualHost> 直下に書くとき、パターンがマッチする対象は /products/123 のように先頭にスラッシュが付きます(だから ^/products/...)。一方 .htaccess に書くときはディレクトリまでの部分が削られるため、スラッシュなしの ^products/... で書きます。ネットのサンプルをコピペして「動かない!」となる原因の定番なので、どちら向けのサンプルかを必ず確認してください。
5. 基本の3点セット:RewriteEngine / RewriteCond / RewriteRule
さきほどの conf に出てきたリライト構文を分解します。ほぼこの3つの命令の組み合わせでできています。
RewriteEngine On # ① リライト機能のスイッチON
RewriteCond %{HTTPS} off # ② 条件:httpsでないなら…
RewriteRule ^(.*)$ https://example.com$1 [R=301,L] # ③ 書き換えルール
① RewriteEngine On
リライト機能の電源スイッチ。これを書き忘れると以降のルールがすべて無視されます(初心者が最初にハマるポイント)。
② RewriteCond(条件)
「次の RewriteRule を適用するかどうか」の条件です。直後の RewriteRule 1つにだけ効きます。
RewriteCond テスト対象 条件パターン
よく使うテスト対象(%{...} で参照します):
| 変数 | 意味 | 例 |
|---|---|---|
%{HTTPS} |
httpsか |
on / off
|
%{HTTP_HOST} |
ホスト名 | www.example.com |
%{REQUEST_URI} |
リクエストされたパス | /products/123 |
%{REQUEST_FILENAME} |
パスをファイルに変換した実体 | /var/www/html/products/123 |
③ RewriteRule(書き換えルール)
本体です。正規表現でURLのパス部分にマッチさせ、書き換え先を指定します。
RewriteRule パターン(正規表現) 書き換え先 [フラグ]
例を分解してみます(VirtualHost内なので先頭に / あり)。
RewriteRule ^/products/([0-9]+)$ /index.php?id=$1 [L]
^/products/([0-9]+)$ /index.php?id=$1 [L]
─────────┬────────── ────────┬─────── ─┬─
パターン: 書き換え先: フラグ:
「/products/数字」に index.php に ここで処理を
マッチしたら id=数字 を付けて渡す 打ち切る
( ) で囲んだ部分が
$1 に入る(後方参照)
つまり /products/123 へのアクセスが、内部的に /index.php?id=123 として処理されます。ユーザーにはきれいなURLを見せつつ、アプリは従来のパラメータ形式で受け取れるわけです。
6. フラグ早見表
[ ] の中に書くオプションで、ルールの動きが変わります。よく使うものだけ覚えれば十分です。
| フラグ | 読み方 | 意味 |
|---|---|---|
[L] |
Last | このルールにマッチしたら、以降のルールを見ない(処理打ち切り) |
[R=301] |
Redirect | 内部転送ではなくリダイレクトにする。301=恒久移転 |
[R=302] |
〃 | 302=一時的な移転(メンテナンス誘導など) |
[NC] |
No Case | 大文字小文字を区別しない |
[QSA] |
Query String Append | 元のURLに付いていた ?a=1 などを書き換え先にも引き継ぐ |
[F] |
Forbidden | 403 Forbidden を返す(アクセス拒否) |
301と302の使い分けポイント。
- 301(恒久): サイト移転・URL設計変更。検索エンジンが「新URLが正」と学習し、SEO評価が引き継がれる
- 302(一時): メンテナンス画面への誘導など。検索エンジンは元のURLを覚えたままにする
7. 新旧URLは1件ずつ全部書くの?【よくある誤解】
リライトと聞くと「旧URLと新URLの対応表を1件ずつ書くのかな?」とイメージしがちですが、基本は書きません。状況によって3つの手段を使い分けます。
手段A: 法則があるなら、正規表現1行で一括変換(基本形)
ブログ記事が1万件あっても、URLの変換に法則があるなら書くルールは1行です。
# /old-blog/xxx → /blog/xxx (記事が何万件あってもこの1行)
RewriteRule ^/old-blog/(.*)$ /blog/$1 [R=301,L]
/old-blog/entry-123 → /blog/entry-123
/old-blog/2025/summer → /blog/2025/summer
/old-blog/foo/bar → /blog/foo/bar
└── (.*) が捕まえた部分が $1 にそのまま入る
「対応表」を書くのではなく 「変換の法則」を書く——これがmod_rewriteの本質です。
手段B: 法則がなく件数が少ないなら、個別に列挙
サイトリニューアルでURL構造が変わり対応に法則がない、でも数件〜数十件——という場合は個別に書きます。この用途なら正規表現不要の Redirect 命令(mod_alias)で十分です。
# 単純な1対1リダイレクトの列挙
Redirect 301 /about-us.html /company/profile
Redirect 301 /price.html /service/pricing
Redirect 301 /old-contact.html /support/contact
手段C: 法則がなく大量なら、対応表を外部ファイルに(RewriteMap)
対応が数百〜数万件あるなら、confに列挙するのは現実的でないので、対応表を別ファイルに逃がします。
# /etc/httpd/redirect-map.txt
# 「旧パス 新パス」を並べただけのテキストファイル。何千行でもOK
about-us.html /company/profile
price.html /service/pricing
old-contact.html /support/contact
# conf側は3行だけ。対応表の中身はApacheが引いてくれる
RewriteEngine On
RewriteMap oldnew txt:/etc/httpd/redirect-map.txt
RewriteRule ^/(.*)$ ${oldnew:$1} [R=301,L]
対応表の管理(追加・修正)とApacheの設定を分離できるので、大規模サイト移転の定石です。
使い分けまとめ
| 状況 | 手段 |
|---|---|
| 新旧に法則がある(ディレクトリ名変更など) |
A: RewriteRule の正規表現1行 |
| 法則がなく、数件だけ |
B: Redirect 301 を個別列挙 |
| 法則がなく、大量にある |
C: RewriteMap で対応表を外部ファイル化 |
まとめ: 「基本は正規表現のパターンで一括処理します。法則化できない対応が大量にある場合はRewriteMapで対応表を外部ファイルに出します」
8. 実務でよく見る鉄板レシピ
第4章のconfに出てきたものも含め、頻出パターンをまとめます(VirtualHost内に書く前提の表記です)。
レシピ1: http → https の強制(ほぼ全サイトで使う)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
httpsでなければ、同じホスト・同じパスのhttpsへ301リダイレクト。
レシピ2: www あり/なし の統一
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com$1 [R=301,L]
同じ内容が2つのURLで見えると検索エンジンに「重複コンテンツ」と判定されうるため、どちらかに寄せるのが定石です。
レシピ3: きれいなURL(クエリ文字列を隠す)
RewriteEngine On
RewriteRule ^/products/([0-9]+)$ /product.php?id=$1 [L,QSA]
/products/123→ 内部的に/product.php?id=123。[QSA]を付けると/products/123?sort=priceのsort=priceも引き継がれます。
レシピ4: メンテナンスページへの誘導
RewriteEngine On
RewriteCond %{REMOTE_ADDR} !^203\.0\.113\.10$ # 自分のIPは除外(! は否定)
RewriteCond %{REQUEST_URI} !^/maintenance\.html$ # メンテページ自体は除外(無限ループ防止)
RewriteRule ^(.*)$ /maintenance.html [R=302,L]
ポイントは2つ。自分(確認作業する人)のIPは除外すること、メンテページ自身を書き換え対象から外すこと(外さないと maintenance.html → maintenance.html → … の無限ループになります)。一時的なので302を使います。
9. SPA(モダンフロントエンド)でも実は使っている
Vue や React で作ったSPA(シングルページアプリ)をApacheで配信する場合、こういう設定を書きます。
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f # 実在するファイルではない
RewriteCond %{REQUEST_FILENAME} !-d # 実在するディレクトリでもない
RewriteRule ^ /index.html [L] # なら全部 index.html に渡す
SPAは「どのURLで来ても、まず index.html を返してJavaScriptに画面遷移を任せる」構造なので、このフォールバック設定がないと、ページを再読み込みしたときに404になるという有名なトラブルが起きます。「SPAでF5を押すと404になるんですが」→「Apacheのrewrite設定が抜けてますね」は現場あるあるです。
10. 動かないときのデバッグ方法
リライト設定は「書いたのに効かない」がとにかく多いです。調べ方を知っているかどうかで差がつきます。
リライトのログを出す(Apache 2.4以降)
LogLevel alert rewrite:trace3
エラーログに、どのルールがどう評価されたかが1ステップずつ出力されます(trace1〜trace8で詳しさが変わります。本番で出しっぱなしにするとログが膨大になるので、調査が終わったら必ず戻すこと)。
チェックリスト
-
RewriteEngine Onを書いたか - パターン先頭の
/の有無は書く場所と合っているか(第4章末尾の罠を参照) -
.htaccessの場合、conf側でAllowOverride All(またはFileInfo)が許可されているか ← .htaccessが丸ごと無視される原因の定番 - confに書いた場合、
apachectl configtest→systemctl reload httpdをしたか - ブラウザが301をキャッシュしていないか(Chromeは301を強く覚えます。検証はシークレットウィンドウか
curlで)
# curlならキャッシュの影響を受けずに確認できる
curl -I http://example.com/old-page
# → HTTP/1.1 301 Moved Permanently
# → Location: https://example.com/new-page
11. まとめ
Q. Apacheのリライト設定とは何ですか?
mod_rewriteモジュールを使って、リクエストされたURLを正規表現で判定し、別のURLに書き換える機能です。書き換えには、ブラウザに新URLを通知して再リクエストさせる「リダイレクト(301/302)」と、サーバー内部で処理先だけ差し替える「内部転送」の2種類があります。設定はサイトごとのconfファイル(VirtualHost内)か.htaccessに記載します。
Q. どんな場面で使いますか?
http→httpsの強制、wwwあり/なしの統一、サイト移転時の301リダイレクト、クエリ文字列を隠したきれいなURLの実現、SPA配信時のindex.htmlフォールバックなどです。
Q. 301と302の違いは?
301は恒久的な移転で、検索エンジンの評価が新URLに引き継がれます。302は一時的な移転で、メンテナンス誘導などに使います。
Q. サイト移転で大量のURLが変わる場合、全部設定に書くのですか?
新旧URLに法則があれば正規表現1行で一括対応します。法則がない対応が大量にある場合は、RewriteMapで対応表を外部のテキストファイルに切り出して管理します。
Q. CDN(Cloudflare等)があればWebサーバーは不要では?
CDNはエッジでのキャッシュ・防御、Webサーバーはオリジン側でのアプリへの振り分けと、担当する層が違うので共存します。ただしhttps強制やリダイレクト、静的配信といった一部の役割はCDN側に寄せられます。
ここまで答えられれば、「設定ファイルを触ったことがある人」として十分通用します。あとは実際に手元のDocker等でApacheを立てて、レシピを1つずつ試してみるのがおすすめです。