URLの「?」(クエリ文字列)と「#」(フラグメント)の違い
まず、要点
- フラグメントのみの変更は再読み込みなしで履歴が追加される
- クエリ文字列やパスの変更はサーバーへの新規リクエストを伴う
- pushState等でのハッシュ変更はhashchangeが発火しない
このノートの内容
クエリ文字列とフラグメントは何が違う?
1・2行目はサーバー送信の有無をクエリ文字列とフラグメントで対比する。3・4行目はフラグメント側の主な用途と変更時の挙動の特徴を示す(対応するクエリ文字列側の値は本文の要約表を参照)。
| 対象 | 役割・違い |
|---|---|
| サーバーへの送信(クエリ文字列) | クエリ文字列はリクエストの一部としてサーバーへ送信され、サーバー側処理に使われる。 |
| サーバーへの送信(フラグメント) | フラグメントはリクエストとしてサーバーに送信されず、ブラウザ側だけで処理される。 |
| 主な用途(フラグメント) | フラグメントは文書内の要素へのスクロールなど、リソース内の位置指定に使われる。 |
| 変更時の挙動(フラグメント) | フラグメントのみの変更は同じ文書のまま、再読み込みなしで履歴エントリが追加される。 |
ここがポイント:送信有無だけでなく、用途や遷移時の挙動もクエリ文字列とフラグメントで異なる。
前提・位置づけ:MDNの一次資料に基づく既存claimの整理であり、新しい事実は追加していない。
調査結果
結論
URLの「?」以降(クエリ文字列)と「#」以降(フラグメント)の最大の違いは、リクエスト時にサーバーへ送信されるかどうかにある。クエリ文字列はサーバーに送られてサーバー側処理に使われるが、フラグメントはサーバーに送られず、ブラウザなどクライアント側だけで処理される(MDNのURL解説[1]、MDNのフラグメント解説[2])。
?(クエリ文字列)とは
?key1=value1&key2=value2 の形式で、&区切りのキー/値の組としてウェブサーバーに渡される追加の引数。サーバー側のプログラムはリソースを返す前にこれらの値を使って絞り込みや処理を行うことができる(MDNのURL解説[1])。
#(フラグメント)とは
#SomewhereInTheDocument のように、取得済みのリソース内の特定の部分(いわば「ブックマーク」)を指す部分。id属性を持つ要素へのスクロール、テキストフラグメント(該当テキストの強調表示)、メディアフラグメント(動画・音声の再生開始位置指定)など、文書内の「どこを見るか」を示すために使われる(MDNのフラグメント解説[2])。
サーバーへ送られるかどうかの違い
- クエリ文字列: リクエストの一部としてサーバーへ送信され、サーバー側の処理(検索条件の絞り込みなど)に使われる。
- フラグメント: リクエストの一部としてサーバーには送信されない。リソースがブラウザに届いたあと、ブラウザ側だけで解釈・処理される(MDNのURL解説[1]、MDNのフラグメント解説[2])。
この性質上、サーバーのアクセスログにフラグメントの値が残ることは基本的にない(サーバー自体がその値を受け取らないため)。
履歴・リロード時の挙動の違い
- フラグメントのみを変更する操作(
location.hash = "foo"のような設定)は、同じ文書(document)を維持したまま行われ、ブラウザの履歴には新しい項目が追加されるが、ページ全体の再読み込みは発生しない(MDNの履歴API解説[3])。 - クエリ文字列やパスを変えた通常のURL遷移は、別のリソースとしてサーバーへ新規にリクエストが送られる、いわゆる「ページ遷移」になる。
- フラグメントの変更はhashchangeイベントで検知できるが、
history.pushState()やhistory.replaceState()でハッシュを書き換えた場合はhashchangeイベントが発生しないという違いもある(MDNの履歴API解説[3])。 - ページを手動でリロード(再読み込み)した場合、クエリ文字列を含むURLは改めてサーバーへリクエストとして送られ、サーバー側の処理も再実行される。一方フラグメントはもともとサーバーに送られる情報ではないため、リロードしてもサーバー側の処理には影響せず、URL上に保持された値はブラウザが該当箇所への表示・スクロールに使うだけになる(この点はクエリ文字列・フラグメントそれぞれの送信有無から論理的に導かれる整理であり、リロード動作そのものを直接説明した一次資料の記述ではない点に留意)。
まとめ
| 項目 | ?(クエリ文字列) | #(フラグメント) |
|---|---|---|
| サーバーへの送信 | される | されない |
| 主な用途 | サーバー側での絞り込み・処理 | 文書内の位置指定(スクロール等) |
| 変更時の挙動 | 通常は新規リクエストを伴うページ遷移 | 同一文書内での遷移、再読み込みなし |
| 履歴への記録 | 通常のナビゲーションとして記録 | 履歴エントリは追加されるが文書は同じ |
注意事項・未確認事項
- フラグメントのみを変更した場合に『ページ全体の再読み込みが起きない』ことを直接一文で明言したMDNの記述は今回確認できなかった。MDNの履歴API解説にある『window.location="#foo"の設定では同じdocumentのままハッシュの変更のみが可能』という記述などを組み合わせた推論であり、単一の一次資料による明示的確認ではない点に留意されたい。
- 手動でのページ再読み込み(F5等)時に、クエリ文字列がサーバーへ再送されフラグメントは影響しないという整理は、『クエリ文字列は送信される/フラグメントは送信されない』という確認済みの事実からの論理的な帰結であり、リロード動作そのものを直接解説した一次資料は今回確認できていない。
- SPA(シングルページアプリケーション)のルーターがhistory.pushState/replaceStateでクエリ文字列やパスまで書き換えて、サーバーへの新規リクエストなしに見た目上のページ遷移を行うケースなど、フレームワーク実装による例外的な挙動は本レポートの範囲外としている。