URLのフラグメント識別子(#以後の文字列)でページを振り分ける必要がありました。 この場合サーバでなくブラウザで振り分ける必要があります。
経緯は、もともと同じページ内でスクロールさせるつもりだったが別ページに分ける仕様に変わった。
既にハッシュタグ#付きのURLはQRコードで印刷されていたため、システムを修正しました。(期限がなくて大変だった。。)
この記事はその備忘録です。
フラグメント識別子(fragment identifier)
URLの#に続く文字列はフラグメント識別子です。RFC3986
よく、目次やページ内のリンクで使われています。
タグのidと同じ文字列を指定すると、ページ内の該当箇所にスクロールしてくれます。
これはアンカーリンク(anchor link)やハッシュリンクとも呼ばれている。
フラグメント識別子はサーバに届かない
当初の考えではApacheで#を/に変換してリダイレクトさせる予定でしたが、設定してもリダイレクトされない。
アクセスログのURLには#から先がなくなっていました。
明確な挙動について読み解けなかったんですが、#より後ろの文字列はサーバに届かないようです。
URI#fragent?query=1
このようにクエリパラメータを#以後に付けてしまうとサーバまで届かないのでご注意を。
フラグメント識別子によってページを振り分ける構成
フラグメント識別子はサーバに届かないことが分かりました。
そこで、#より前のURLのページを用意しブラウザからリダイレクトさせるようにします。
例として、次のURLで考えます。
https://host.domain.com/page#target1
フラグメント識別子はtarget1、target2、target3とあり、3つのページにリダイレクトさせます。
https://host.domain.com/pageはリダイレクトするだけのページです。
https://host.domain.com/pageのページを用意するhttps://host.domain.com/page/target1、https://host.domain.com/page/target2、https://host.domain.com/page/target3のページを用意するhttps://host.domain.com/pageではフラグメント識別子毎に各ページへリダイレクトさせる
https://host.domain.com/page#target1でアクセスするとサーバと2往復することになってしまいますが、用意したページに辿りつけるため良しとします。
ブラウザでリダイレクトする
https://host.domain.com/pageの<head>に以下のスクリプトを書きます。
<script type="text/javascript">
console.log(location.href); // https://host.domain.comn/page#target1
console.log(location.hash); // #target1
if(location.hash.length == 0 || location.hash === '#'){
// ②このページはリダイレクトするのみ
// block redirect loop
location.href = '/';
}else{
// ②各ページへリダイレクトする
location.href = location.href.replace('#', '/');
}
</script>
②では、URLを調べ#を/に変えてリダイレクトさせています。
フラグメント識別子がなくとも、#はブラウザに残ります。
そのため、②のみだとURLがhttps://host.domain.com/page#の場合にリダイレクトループしてしまいます。
これを回避するためトップページへリダイレクトしていますが、404や#を除くだけの処理に変えても良さげ。
location.hashはURLのうち、#とそれに続くフラグメント識別子を収めた文字列です。
おわりに
ギリギリ期限に間に合いました。 URLの修正は大変ですので、早めに決めて連絡下さい。お願いします。
参考
- RFC3986
- 3.5. と4.1. が該当箇所。
- 残念ながら、タグにidがないためフラグメント識別子が使えない
- RFC3986 日本語訳の複製 #section-3.5
- こちらはフラグメント識別子が使える!
- ハイパーリンクを提供する - The Web KANZAKI
- フラグメント識別子に関してこの記事の解説が詳しい
- Location - MDN
- URL - MDN
- MDNでも
#に続くURLの文字列はフラグメント識別子と明記
- MDNでも