Apache(.htaccess)の404トラブル切り分け:404が出ない/200になる/トップだけ表示されない

「404が出ない」「404画面なのに200」「トップだけ表示されない」を構造的に理解する。

はじめに

本記事では、一般的なレンタルサーバー(例:Xserver、さくらインターネット、ロリポップ など)において「公開ルート直下に .htaccess を置き、実体はサブディレクトリに配置する」構成で発生するトラブルを構造的に整理します。

扱う内容は Apache(mod_rewrite / ErrorDocument)の挙動に依存するため、同様の構成を取る多くの環境で再現可能です。


前提構成(よくある運用)

  • 公開ルート直下に .htaccess の設置
  • 実際のサイトデータは {document_root}/act3inc.com/ ディレクトリに配置
  • URLは https://act3inc.com/ のまま運用する構成(URLは変えず、内部的にサブディレクトリへマッピング)

※ 本記事内の {document_root} は説明用のプレースホルダです。Apache の DocumentRoot(公開ディレクトリ)を指します。なお、.htaccess の条件式では Apache の変数 %{DOCUMENT_ROOT} を使用します。


症状(よくある発生例)

  1. 存在しない URL にアクセスしても 404 にならない
  2. 404 ページの見た目は表示されるが、HTTP ステータスは 200(いわゆる「ソフト404」)
  3. トップページ(/)だけ表示されない
  4. 他のページは表示される

先に結論:ErrorDocument は「404を発生させる仕組み」ではない

ErrorDocument404 が発生したあとに表示するページを指定するだけで、404 そのものを発生させる仕組みではありません。


原因①:すべてを内部マッピングし、結果として 404 が発生しない構造

典型的に、次のような catch-all の内部マッピングがある構造の場合、

RewriteRule ^(.*)$ /act3inc.com/$1 [L]

存在しないURLも {document_root}/act3inc.com/ 側に渡されます。

ここで /act3inc.com 側に「index.html / index.php で受け口がある」「CMS/SPAが常に200を返す」等があると、Apache視点ではリクエストを“処理できてしまう”ため、404 が発生しません(= ErrorDocument 404 も呼ばれません)。


原因②:トップページだけ表示されない(REQUEST_FILENAME -d の構造的な問題)

多くの .htaccess テンプレートにある「既存ファイル/既存ディレクトリはそのまま処理する」構造は便利ですが、

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

/ の場合、%{REQUEST_FILENAME} は公開ルートの物理ディレクトリを指すため -d が真となり、処理がそこで停止します。

その結果、トップページだけ内部マッピングに到達できず「トップだけ表示されない」現象が発生します。


対処:ルートを最優先にし、マッピング前に“実体チェック”して、無ければ 404 を発生させる

ポイントは次の3点です。

  1. ルート(^$)を最優先で新サイトへ流すようにする
  2. サブディレクトリ側に実体がある場合だけ内部マッピングする
  3. 実体がない場合は 明示的に 404 を返すようにする

構成例(.htaccess)

index.html は構成に合わせて index.php などに変更してください。

RewriteEngine On

# 1) ループ防止(新サイト配下は触らない)
RewriteCond %{REQUEST_URI} ^/act3inc\.com/ [NC]
RewriteRule ^ - [L]

# 2) ルートは最優先で新サイトへ(ここが重要)
RewriteRule ^$ /act3inc.com/index.html [L]

# 3) 既存ファイル/既存ディレクトリはそのまま(ただしルートは除外)
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# 4) 新サイト側に実体がある場合のみ内部マッピング(URLは変えない)
RewriteCond %{DOCUMENT_ROOT}/act3inc.com/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/act3inc.com/$1 -d
RewriteRule ^(.*)$ /act3inc.com/$1 [L]

# 5) 実体がなければ 404 を返す(ErrorDocument が表示される)
RewriteRule ^ - [R=404,L]
ErrorDocument 404 /act3inc.com/404/index.html

動作確認のポイント

ブラウザ表示だけでは判定できないため、必ず HTTPステータスを確認してください。

  • 存在しないURLが 404 となっているか
  • 404画面が表示されていても HTTPステータスが200になっていないか

Chrome DevTools の Network タブや curl -I などで確認することを推奨します。