サーバーの設定ファイル、.htaccess。
名前は聞いたことがあっても、中を開くと記号だらけで、私は何が書いてあるのか全然わかりませんでした。
先日、すでに公開されているLPで、いろいろなエリアのページがアップロードされている中から、特定のエリアだけを検索エンジンにインデックスさせたいということがありました。
そのための.htaccessの書き方を教えてもらったのですが、コードを見ても何をしているのかさっぱりで…
自分の理解のためにも、.htaccessの基本から今回のコードの意味まで、一つずつ整理してまとめました!
.htaccessってなに?
サーバーへの指示書のようなファイル
.htaccess(エイチティーアクセス)は、Webサーバーに「このフォルダではこう動いてね」と指示を出すための設定ファイルです。
といったことができます。
ファイル名が「.」(ドット)から始まっていて、拡張子のように見える部分がそのままファイル名になっているのが特徴です。
置いたフォルダとその下の階層に効く
.htaccessは、置いたフォルダと、その中にあるフォルダすべてに効きます。
サイトのいちばん上の階層(ドキュメントルート)に置けば、サイト全体に効くということです。
今回のコードも、サイトのいちばん上に置く前提で書かれています。
使えるサーバー・使えないサーバーがある
.htaccessを読み込むのは、ApacheやLiteSpeedというWebサーバーです。
国内の一般的なレンタルサーバーの多くはこちらに対応しているようです。
一方で、Nginxという種類のサーバーは.htaccessを読み込まないので、同じ書き方では動きません。
.htaccessを触る前に知っておきたいこと
.htaccessは、1文字間違えただけでサイト全体が表示されなくなることがあります。
よくあるのが「500 Internal Server Error」という真っ白なエラー画面です。
なので、書き換える前に元のファイルをダウンロードして、バックアップを取っておくと安心だと思います。
エラーが出たら、バックアップのファイルに戻せばひとまず元通りになります。
もうひとつ、Macを使っている場合の話です。
ドットから始まるファイルは「隠しファイル」扱いになるので、Finderでは普段は見えません。
Finderで「command + shift + .(ピリオド)」を押すと、隠しファイルの表示・非表示を切り替えられます。
.htaccessの基本の書き方
1行に1つの指示を書く
.htaccessは、基本的に「命令の名前 + 設定する内容」を1行ずつ書いていく形です。
たとえば、古いページを新しいページへ転送(301リダイレクト)する書き方はこうなります。
Redirect 301 /old.html https://example.com/new.html
「Redirect」が命令の名前で、「301」が転送の種類、そのあとに転送元と転送先が続いています。
#から始まる行はメモ
行の先頭に「#」を付けると、その行はサーバーに無視されます。
これをコメントといって、人間向けのメモとして使います。
今回教えてもらったコードにも、「今後エリアを増やすときはここを書き換える」といったメモが#付きで書かれていました。
<〜>で囲むと「この条件のときだけ」になる
HTMLのタグのように、<〜>と</〜>で囲む書き方もあります。
これは、囲んだ中の設定を、ある条件を満たしたときだけ動かすという意味です。
今回のコードに出てくるのは次の2つです。
| 書き方 | 意味 |
|---|---|
| <IfModule 機能名>〜</IfModule> | その機能(モジュール)がサーバーで使えるときだけ、中の設定を動かす。使えない場合は無視されるので、エラーで止まるのを防げる |
| <FilesMatch “条件”>〜</FilesMatch> | ファイル名が条件に当てはまるときだけ、中の設定を動かす |
今回やりたかったこと:一部のエリアだけインデックスさせる
今回のLPは、次のようなフォルダ構成になっていました(名前はサンプルです)。
/(トップページ)
├ about.php
├ service.php
├ price.php
├ area-a/
│ ├ about.php
│ └ price.php
├ area-b/
│ ├ city-a/
│ ├ city-b/
│ └ city-c/ ← インデックスさせない
└ area-c/ ← インデックスさせない
エリアごとのページはたくさんアップロードされているけれど、検索結果に出したいのは一部のエリアだけ、という状態です。
noindexってなに?
検索エンジンに「このページは検索結果に出さないでね」と伝える指示をnoindex(ノーインデックス)といいます。
ページ自体は消えないので、URLを知っている人やリンクから来た人は、今まで通りページを見ることができます。
noindexを伝える方法は、HTMLの中に書く方法と、サーバーが返す情報(レスポンスヘッダー)に付ける方法があります。
ヘッダーに付けるときはX-Robots-Tagという名前を使います。
ページが多いと1ファイルずつHTMLを書き換えるのは大変なので、.htaccessでまとめてヘッダーを付ける、というのが今回のやり方です。
「noindex, follow」の意味
これは、このページは検索結果に出さないけれど、ページ内のリンクはたどっていいよという意味です。
検索に出さないページからも、公開しているページへのリンクはたどってもらえるようにしている形です。
1つ目のコードを読み解く
まずは1つ目のコードです(エリア名・ページ名はサンプルに置き換えています)。
<FilesMatch "\.html$">
AddType application/x-httpd-php .html
</FilesMatch>
<IfModule mod_setenvif.c>
SetEnvIf Request_URI "^/$" seo_indexable
SetEnvIf Request_URI "^/(area-a|area-b|area-b/(city-a|city-b))/$" seo_indexable
SetEnvIf Request_URI "^/(about|service|price|flow|faq)\.php$" seo_indexable
SetEnvIf Request_URI "^/(area-a|area-b|area-b/(city-a|city-b))/(about|service|price|flow|faq)\.php$" seo_indexable
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(php|html?)$">
Header always set X-Robots-Tag "noindex, follow" env=!seo_indexable
</FilesMatch>
</IfModule>
大きく分けると3つのパートになっています。
【パート①】HTMLファイルでPHPを動かす設定
<FilesMatch "\.html$">
AddType application/x-httpd-php .html
</FilesMatch>
この部分はインデックスの話とは別で、拡張子が.htmlのファイルでも、中に書いたPHPを動かせるようにする設定です。
「\.html$」は「.htmlで終わるファイル」という意味で、そのファイルをPHPとして扱うようサーバーに伝えています。
この書き方はサーバー会社によって違うことがあるようです。
【パート②】インデックスしていいページに目印を付ける
ここが今回のいちばん大事なところです。
SetEnvIf Request_URI "^/$" seo_indexable
SetEnvIfは、「もし条件に当てはまったら、目印(環境変数)を付ける」という命令です。
この1行を分解するとこうなります。
| 部分 | 意味 |
|---|---|
| SetEnvIf | 条件に当てはまったら目印を付ける命令 |
| Request_URI | アクセスされたURLのうち、ドメインより後ろの部分(/area-a/ など)を調べる |
| “^/$” | 条件。「/」だけのURL、つまりトップページ |
| seo_indexable | 付ける目印の名前。名前は自由に決められる |
つまり、「トップページにアクセスされたら、seo_indexable(インデックスしていい)という目印を付ける」という意味です。
この目印を付ける行が4行あって、それぞれ対象のページが違います。
1行目はトップページ、2行目は公開するエリアのトップページ、3行目はいちばん上の階層にある公開ページ、4行目は公開するエリアの中にある公開ページです。
正規表現の記号の意味
条件の部分には正規表現という、文字のパターンを表す書き方が使われています。
正規表現についてまとめたページがありますので気になる方はご参照ください。

記号だらけに見えて一番わからなかったところなので、今回出てくる記号をまとめました。
| 記号 | 意味 | 例 |
|---|---|---|
| ^ | ここから始まる | ^/ →「/」から始まる |
| $ | ここで終わる | \.php$ →「.php」で終わる |
| | | または | area-a|area-b →「area-a」または「area-b」 |
| ( ) | ひとまとまりにする | (about|price) →「about」か「price」のどちらか |
| \. | ただの「.」(ドット) | 正規表現では「.」だけだと「どんな1文字でもいい」という意味になるため、\を付けて文字の「.」として扱う |
| ? | 直前の1文字(またはまとまり)があってもなくてもいい | html? →「htm」でも「html」でもいい |
これを踏まえて、2行目を読んでみます。
SetEnvIf Request_URI "^/(area-a|area-b|area-b/(city-a|city-b))/$" seo_indexable
「/」で始まり、そのあとが「area-a」「area-b」「area-b/city-a」「area-b/city-b」のどれかで、最後が「/」で終わるURL、という意味になります。
当てはまるのは、/area-a/、/area-b/、/area-b/city-a/、/area-b/city-b/ の4つです。
area-c や area-b/city-c はリストに入っていないので、目印は付きません。
4行目は、この2行目と3行目をくっつけた形です。
SetEnvIf Request_URI "^/(area-a|area-b|area-b/(city-a|city-b))/(about|service|price|flow|faq)\.php$" seo_indexable
公開するエリアのフォルダの中にある、about.php や price.php などの決まったページだけに目印が付きます。
/area-a/price.php には目印が付きますが、/area-c/price.php には付きません。
【パート③】目印のないページにnoindexを付ける
<IfModule mod_headers.c>
<FilesMatch "\.(php|html?)$">
Header always set X-Robots-Tag "noindex, follow" env=!seo_indexable
</FilesMatch>
</IfModule>
外側から順番に読むとこうなります。
<IfModule mod_headers.c> は、ヘッダーを操作する機能が使えるときだけ動かす、という意味です。
<FilesMatch “\.(php|html?)$”> は、.php、.html、.htm で終わるファイルだけが対象、という意味です。
画像やCSSにはnoindexを付けない、ということですね。
そして中の1行がこちらです。
| 部分 | 意味 |
|---|---|
| Header always set | サーバーが返すヘッダーに情報を付ける(always は、どんな応答のときでも付けるという意味) |
| X-Robots-Tag “noindex, follow” | 付ける情報。検索結果に出さない、リンクはたどっていい |
| env=!seo_indexable | 条件。「!」は「〜ではない」という意味なので、seo_indexableの目印が付いていないときだけ |
パート②で目印を付けたページはそのまま、目印が付いていないページにだけnoindexが付く、という仕組みです。
許可するページを先にリストアップしておいて、リストにないものは全部noindex、という考え方ですね。
2つ目のコードを読み解く
同じことをする別の書き方も教えてもらいました。
<IfModule mod_headers.c> <FilesMatch "\.(php|html?)$"> Header always set X-Robots-Tag "noindex, follow"# トップページ(サーバー内部のindexファイルも許可) Header always unset X-Robots-Tag "expr=%{REQUEST_URI} =~ m#^/(?:index\.(?:php|html?))?$#" # ルート直下の公開ページ Header always unset X-Robots-Tag "expr=%{REQUEST_URI} =~ m#^/(?:about|service|price|flow|faq)\.php$#" # 公開するエリアのトップページ Header always unset X-Robots-Tag "expr=%{REQUEST_URI} =~ m#^/(?:area-a|area-b(?:/(?:city-a|city-b))?)/(?:index\.(?:php|html?))?$#" # 公開するエリア直下の公開ページ Header always unset X-Robots-Tag "expr=%{REQUEST_URI} =~ m#^/(?:area-a|area-b(?:/(?:city-a|city-b))?)/(?:about|service|price|flow|faq)\.php$#"</FilesMatch>
</IfModule>
考え方が逆:いったん全部にnoindex → 許可するページだけ外す
1つ目は「許可するページに目印を付けて、目印がないページにnoindexを付ける」流れでした。
2つ目は、最初に全部のページへnoindexを付けて、許可するページだけあとから外すという流れです。
「Header always set」で付けて、「Header always unset」で外しています。
unsetは「取り消す」という意味です。
結果として同じ状態を目指していますが、1つ目のように目印(SetEnvIf)を使わないので、ヘッダーの設定だけで完結しています。
expr= と m#〜# の意味
unsetの行の後ろにある “expr=〜” は、この条件に当てはまるときだけunsetするという意味です。
中身を分解するとこうなります。
| 部分 | 意味 |
|---|---|
| expr= | ここから条件式を書く、という合図 |
| %{REQUEST_URI} | アクセスされたURL(ドメインより後ろの部分) |
| =~ | 左側が右側のパターンに当てはまるか |
| m#〜# | #と#の間が正規表現。区切り記号に#を使っているので、URLの「/」と区別しやすい |
この expr= を使った書き方は、Apache 2.4系の比較的新しいバージョンで使えるものです。
古いサーバーだと動かない可能性があるので、使う前にサーバーのバージョンを確認しておくと安心だと思います。
(?: ) は「まとめるだけ」の書き方
2つ目のコードには「(?:」という見慣れない記号が出てきます。
これは、( ) と同じように「ひとまとまりにする」けれど、中身を記録しないという書き方です。
今回の使い方では、( ) と (?: ) のどちらでも結果は同じなので、「まとめるだけ」と読んでしまって大丈夫です。
もうひとつ、エリアの部分の書き方も少し変わっています。
(?:area-a|area-b(?:/(?:city-a|city-b))?)
「area-a」または「area-b」で、area-bのあとには「/city-a」か「/city-b」が付いていてもいなくてもいい、という意味です。
当てはまるのは1つ目と同じ、area-a、area-b、area-b/city-a、area-b/city-b の4つです。
index.php も許可している理由
2つ目のコードには、1つ目にはなかった「index.php」「index.html」が入っています。
m#^/(?:index\.(?:php|html?))?$#
これは「/」だけ、または「/index.php」「/index.html」「/index.htm」を許可する、という意味です。
トップページ(/)にアクセスされたとき、サーバーは裏側で index.php や index.html を探して表示しています。
調べた範囲では、この裏側の動きのときにURLが「/index.html」のように扱われることがあり、1つ目の書き方だとサーバーの環境によってはトップページにもnoindexが付いてしまう可能性があるようです。
2つ目のコードのメモに「サーバー内部のindexファイルも許可」と書かれているのは、この動きに対応するためだと思います。
どちらの書き方を使う場合も、設定したあとにトップページのヘッダーを確認しておくと安心です(確認方法は後半で書いています)。
1つ目と2つ目の違いを表で比較
| 1つ目 | 2つ目 | |
|---|---|---|
| 考え方 | 許可するページに目印 → 目印がないページにnoindex | 全部にnoindex → 許可するページだけ外す |
| 使っている命令 | SetEnvIf と Header set | Header set と Header unset |
| index.php などの扱い | リストに入っていない | リストに入っている |
| 必要な機能 | mod_setenvif と mod_headers | mod_headers(expr= が使えるApache 2.4系) |
| HTMLでPHPを動かす設定 | 含まれている | 含まれていない(別途残す必要がある) |
2つ目のコードは、1つ目の後半(noindexを付ける部分)の書き換えなので、HTMLでPHPを動かす設定を使っている場合はそちらを消さないように注意が必要です。
実際にどのURLがどうなるか
今回のサンプルのフォルダ構成だと、結果はこうなります。
| アクセスされたURL | 結果 | 理由 |
|---|---|---|
| / | インデックスされる | トップページとして許可 |
| /price.php | インデックスされる | いちばん上の階層の公開ページとして許可 |
| /area-a/ | インデックスされる | 公開エリアのトップページとして許可 |
| /area-b/city-a/price.php | インデックスされる | 公開エリアの中の公開ページとして許可 |
| /area-c/ | noindex | area-c がリストにない |
| /area-b/city-c/price.php | noindex | city-c がリストにない |
| /area-a/campaign.php | noindex | campaign.php がページのリストにない |
| /images/sample.jpg | 何も付かない | .php・.html以外なので対象外 |
エリアだけでなく、ページ名もリストにないとnoindexになるところがポイントです。
あとから新しいページを作ったときは、ページ名のリストにも追加しないと検索に出てこない、ということになります。
エリアやページを増やしたいときに書き換える場所
1つ目のコードのメモにも書かれていた通り、エリアを増やすときはエリア一覧が入っている2か所の正規表現を書き換えます。
たとえば area-c を公開したい場合は、1つ目のコードの2行目と4行目をこう変えます。
SetEnvIf Request_URI "^/(area-a|area-b|area-c|area-b/(city-a|city-b))/$" seo_indexable
SetEnvIf Request_URI "^/(area-a|area-b|area-c|area-b/(city-a|city-b))/(about|service|price|flow|faq)\.php$" seo_indexable
「|area-c」を足しただけです。
片方だけ書き換えると、エリアのトップページは出るのに中のページはnoindexのまま、ということが起きるので、2か所セットで書き換える必要があります。
2つ目のコードの場合も、エリア一覧が入っている下の2行を同じように書き換えます。
あわせて、公開したページのURLを sitemap.xml にも追加しておくと、検索エンジンにページの存在を伝えやすくなります。
設定できたか確認する方法
X-Robots-Tagはページの見た目には出てこないので、ページを開いただけでは設定できているかわかりません。
確認する方法はいくつかあります。
【方法①】ブラウザの開発者ツールで見る
Google Chromeの場合、ページを開いて「右クリック」→「検証」→「Network(ネットワーク)」タブを開きます。
その状態でページを再読み込みして、一覧のいちばん上(ページ本体)をクリックすると、「Response Headers」の中に X-Robots-Tag があるかどうかを見られます。
【方法②】Macのターミナルでcurlコマンドを使う
ターミナルで次のように打つと、そのURLのヘッダーだけを表示できます。
curl -I https://example.com/area-c/
noindexにしたいページで「X-Robots-Tag: noindex, follow」が出ていて、公開したいページでは出ていなければ、狙い通りに動いているということです。
【方法③】Google Search ConsoleのURL検査を使う
Search Consoleを使っているサイトなら、URL検査ツールでインデックスが許可されているかどうかを確認できます。

noindexにしたページを、robots.txtでクロール禁止(Disallow)にしないように注意が必要です。
クロールを禁止すると、検索エンジンがページを読みに来なくなるので、noindexの指示そのものに気づいてもらえなくなります。
その結果、すでに検索結果に出ているページがなかなか消えない可能性があります。
また、noindexを付けてもすぐに検索結果から消えるわけではなく、検索エンジンが次にページを読みに来たタイミングで反映されるようです。
robots.txtの記事は👇

まとめ
.htaccessは、置いたフォルダから下の階層に対して、サーバーの動きを指示する設定ファイルです。
記号だらけで難しく見えましたが、「命令の名前 + 設定内容」の組み合わせと、正規表現の記号の意味がわかると、少しずつ読めるようになりました。
今回のコードは、1つ目が「許可するページに目印を付けて、目印のないページにnoindexを付ける」書き方、2つ目が「全部にnoindexを付けて、許可するページだけ外す」書き方で、やろうとしていることは同じです。
どちらの場合も、エリアやページを増やすときはリストの書き換えを忘れないことと、設定したあとにヘッダーを確認することが大切だと思います。
同じように.htaccessのコードを渡されて困っている方に届いたらうれしいです!


