blog
開発メモサイトをwww統一したはずが、5区画だけ301が素通りしていた——子ディレクトリの.htaccessは親のRewriteを継承しない
受託案件のひとつで、レンタルサーバー上にWordPress・静的HTML・古いアプリが混在するサイトの、非wwwからwwwへの301リダイレクト一本化を任されました。ルートの.htaccessに「HTTPまたは非wwwならhttps://wwwへ301」を1手で追加し、トップページやWordPressのセクションは狙いどおり1回で飛びました。
ところが全区画をcurlで機械的に洗ったところ、1件だけおかしな結果が出ます。
https://example.jp/product/item.html
redirects=0 final=200 url=https://example.jp/product/item.html
非wwwのままHTTP 200が返っています。301がまったく効いていません。ブラウザで見ても普通にページは開くので、リンクを目で辿るだけの確認では気づけない種類の不具合でした。
先に結論から
ルートの.htaccessに301を1本足しても、子ディレクトリが独自にRewriteEngine Onを宣言していると、そのルールは継承されません(Apache公式の仕様)。しかも非wwwのまま200が返るだけなので、ブラウザで見ても壊れて見えず、つい見落とします。だから検証は「ページが開くか(200か)」ではなく、curlのnum_redirectsで「実際にリダイレクトが起きたか」を見ます。加えてWordPressが絡むなら、301を張る前にWordPress側のURL設定を変えないと、サイトが無限リダイレクトで落ちます。手順は記事末にまとめました。
子ディレクトリが独自にRewriteEngineを持つと、親のルールを継承しない
該当ディレクトリは独自の.htaccessを持ち、その中でRewriteEngine Onを宣言していました。Apache公式ドキュメントのRewriteOptions Inheritの項に、この挙動がそのまま書かれています。
In per-directory context this means that conditions and rules
of the parent directory’s .htaccess configuration or <Directory>
sections are inherited.
つまりデフォルトでは継承されないのが仕様です。継承させたければ明示的にRewriteOptions Inheritを書く必要があります(Apache公式:mod_rewrite – RewriteOptions)。今回のサイトはこの指定が無く、子ディレクトリでRewriteEngineを宣言した時点でルートの301ルールは届かなくなっていました。
find . -name .htaccessでサーバー側の構造をあらためて洗うと、同じパターンの区画が計5つ見つかりました。中身はいずれも同一で、しかも厄介なことに「HTTPS化はするが、wwwへの統一はしない」形になっていました。
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
%{HTTP_HOST}をそのまま使い回しているため、非wwwでアクセスすれば非wwwのままHTTPS化するだけで終わります。301が動いているように見えて、統一だけがされない——壊れて見えないぶん、見落としやすい状態でした。
ルートと同じ2条件に子側を書き換えて解決しました。
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.example.jp%{REQUEST_URI} [R=301,L]
Basic認証の区画には、301すら届かない
もう1区画、Basic認証だけがかかった非公開ディレクトリがありました。ここはRewriteEngineを持たないので継承の話とは無関係のはずですが、301ではなく401が返りました。Apacheのリクエスト処理はフェーズ順に進み、認証フェーズがmod_rewriteのfixupより先に走るため、リダイレクトを受け取る前に認証を要求される形になります。今回は非公開領域で実害が無かったため、この区画は保留のまま残しました。
検証は「200が返るか」ではなく「num_redirectsが1以上か」で見る
今回のように、非wwwへアクセスしても200が返ってしまうケースは、ブラウザでリンクをひとつずつ辿る確認では発見できません。非wwwと www の両方に同じ内容の200ページが存在する=重複コンテンツが残っている状態を見逃すことになります。リダイレクト系の変更をルートに加えたときは、次の3手を機械的にやる必要がありました。
find <docroot> -name .htaccessで子ディレクトリを全部洗い出す(リンクされていないディレクトリはURLを推測できないため、サーバー側の構造から洗う)- その中から
RewriteEngineを独自宣言している子を特定する - 実URLで
curl -w "%{num_redirects} %{url_effective}"を叩き、num_redirectsの値そのものを確認する
順序を間違えると、WordPressが無限リダイレクトに陥る
この案件はWordPressが3区画で動いていたため、もう1つ手順上の制約がありました。.htaccess側の301を先に入れ、WordPressのsiteurl/home設定が非wwwのまま残っていると、次のループに入ります。
- 非wwwのURLへアクセス
.htaccessがwwwへ301- WordPressの
redirect_canonicalが、home設定(非www)へ301で押し戻す - 2へ戻る
結果はブラウザのERR_TOO_MANY_REDIRECTSです。「内部リンクを先にwww化してから301を張る」というSEO由来の定石がありますが、WordPressが絡む場合はSEOの都合というよりサイトが落ちるので、WordPress側の設定変更が先、.htaccessの301追加が後という順序がほぼ必須条件になります。
この3点を押さえれば、同じ穴に落ちない
- ルートに301を1本張れば全体に効く、という前提は
.htaccessが1枚しかないサイトでのみ正しい。子ディレクトリが独自にRewriteEngine Onを宣言していると、親のルールは継承されない(Apache公式仕様) - 「200が返るか」だけを見る検証では、非wwwと重複した200ページの残存を見逃す。
num_redirectsを実地で確認するのが要点 - WordPressが絡む場合、
.htaccessの301より先にWordPress側のURL設定を変えないと、設定同士が押し合って無限リダイレクトになる
この案件では、混在した複数のサーバー環境をまたいだコマンド実行そのものにも別の癖があり、そちらはwp-cliをPowerShell経由でレンタルサーバーへ叩くときの地雷にまとめています。「見えている結果を鵜呑みにせず、機械的に実地確認する」という点では、「公開しました」という報告を信じたら本番には何も反映されていなかった話とも根っこが近いと感じています。ここまでに踏んだ地雷をまとめたClaude Code実務運用ガイドもあわせてどうぞ。
※本記事の内容は2026年8月時点のものです。


