1 ポイント 投稿者 GN⁺ 5 시간 전 | 1件のコメント | WhatsAppで共有
  • Chris Morganは自身のサイトで無断のクエリ文字列を全面的に遮断することにしており、現在の実装はCaddyfileに入っている
  • ?ref=example.com のような追跡用パラメータをURLに付けられたくなく、必要なら Referer ヘッダーを見ればよいと考えている
  • ?utm_source=example&utm_*&c.* のような UTM parameters はサイト所有者が使うためのものであり、外部が付けるものではないと考えている
  • 現在このサイトではクエリ文字列をまったく使っておらず、今後使うようになった場合は 既知のパラメータ だけを許可する予定
  • 最終URLは /no-query-strings に決めており、/%3F はCaddyの try_files リライトで問題があるため選ばなかった

無断のクエリ文字列の遮断

  • Chris Morganは自身のサイトで無断のクエリ文字列を全面的に遮断することにした
  • ?ref=example.com のような追跡用パラメータを自分のURLに付けられたくなく、必要なら Referer ヘッダーを見ればよいと考えている
  • ?utm_source=example&utm_*&c.* のような UTM parameters はサイト所有者が使うためのものであり、外部が付けるものではないと考えている
  • 現在このサイトではクエリ文字列をまったく使っておらず、今後使うようになった場合は 既知のパラメータ だけを許可する予定
  • 過去にはスタイルシートURLに ?t=…, ?h=… 形式のキャッシュ無効化URLを使っていたが、そのようなリクエストが壊れても構わないと判断している
  • この遮断は現在Caddyfileに実装されている

URLの選定過程

  • /? を使おうとしていた計画

    • 最初はこのページを https://chrismorgan.info/? に公開したい誘惑が強かった
    • 空のパスと空のクエリという形なので、よくあるが誤った前提を数多く崩し、一部のツールを困らせる可能性があった
    • curl はコマンドラインで末尾の疑問符を不当に削除してしまうように見え、ライブラリ使用はテストしていない
    • 結局、パスという概念を尊重し、人々に寛容であることにし、特にCaddyをすでに十分厄介な方向へ押し進めていると考えた
  • /%3F を使おうとしていた計画

  • 最終的な選択

    • 最終URLは /no-query-strings に決めた
    • /? または /%3F は、将来クエリ文字列に関する別の用途で使う可能性がある

1件のコメント

 
GN⁺ 5 시간 전
Hacker News のコメント
  • 気になったので HTML と URL の W3C 標準を見直してみたが、意外にもクエリ文字列の形式は パーセントエンコーディング 以外には特に定義されていなかった。
    クエリ文字列を “form-urlencoded”[0] のクエリ文字列と混同することはあり得るが、これは相互運用可能な形式の一つにすぎない。一般にクエリ文字列とは URL の ? の後に来る任意のパーセントエンコード文字列[1]であり、応答生成に利用できる HTML URL オブジェクトの別の属性でもある。
    URLSearchParams オブジェクトは form-urlencoded パーサでクエリ文字列を解析した結果だが、これは JavaScript のための相互運用レイヤーにすぎない。
    正直、標準を見る前は反対意見を言うつもりだったが、標準はかなり明確だった。想定外のクエリ文字列に 404 で応答するのも妥当かもしれない。クエリ文字列はパスと同じくらい URL API の一部であり、パスに任意の文字列を付け足すのが望ましくなく未定義の動作だという点には、多くの人が同意できるだろう。
    [0]: https://url.spec.whatwg.org/#application/x-www-form-urlencod...
    [1]: https://url.spec.whatwg.org/#url-class

    • 昔は CMS やフォーラムが index.php だけを置いて、ルーティングをすべて クエリ文字列 で処理するのがかなり一般的だった。
      もちろん form-urlencoded 形式だったし、みんなそこまで野蛮ではなかった。だから index.php?p=homeindex.php?p=shop、あるいは index.php?action=showthread&forum=42&thread=17976 のような URL になっていた。こうした構造では、不明なクエリパラメータに 404 を返すのが正しい応答だとはっきり分かる。
      実際、今でも多くのサイトはそう動いていて、SEO のために Apache/nginx の書き換えルールの背後に隠しているだけだ。
    • その通りで、URL にそれほど多くの意味論があるわけではない。パスは階層データ、クエリは非階層データ向けという意図は明らかで、強い慣習もあり、一部はライブラリが支援または強制しているが、実際のルールはない。
      結局のところ URL は、サーバーがどう処理するかを決める 文字列 にすぎない。
      この議論で本当に面白いのは、404 を返したときの副作用を心配しながら、Web の歴史の中でパスがどれほど長く無意味だったかを完全に忘れていたことだ。今ではパスが勝った。もう /item?id=… のような URL で新規に始めることはほとんどない。いいことだ!
    • 汎用的な 400 の方がよいのではないかと思う。ページが見つからないのではなく、許可されていないリクエストを送ったのだから。
      「リクエストを修正して再試行せよ」と読めるし、自分の提供する API でもそうしている。406 よりこちらを好むのは、こちら側で処理不能な問題ではないからだ。クエリ文字列に何かを付け足して壊そうとした、あるいは文書どおりにリクエストを作らなかったのなら、それはリクエスト側の責任だ。
    • No-Vary-Search 提案では、クエリのどの部分が重要かを指定できる。
      https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
      たとえばキャッシュの観点では、url?a=b&c=durl?c=d&a=b と一致すると見なせる。
    • 標準とは、誰かがどこかに書き留めた、広く受け入れられている振る舞いにすぎない。
      正式な標準として文書化されたことはなくても、従わなければ広範に壊れる慣習は山ほどあるし、文字どおり従うと愚かに見える「標準」も多い。
      元の記事の場合、困るのはそのサイトを訪れようとする人だけで、おそらくブラウザの戻るボタンを押して用事を続けるだろう。その程度の不利益を受け入れるかどうかは本人が決めればいい。ただし、どの標準でも禁止されていないからといって定義上許されるわけではなく、逆に標準が禁止しているからといって急に許されなくなるわけでもない。
  • 理解した限りでは、ほかの Web サイトが著者のサイトへのリンクに ?ref=origin.com のような クエリ文字列 を付けることに苛立っているようだ。
    それが元のサイトにどんな利益をもたらし、著者のサイトにどんな害を与えるのかは分からない。
    両者の振る舞いとも完全に混乱しているように見える。
    広告キャンペーンを運用するときに Google が UTM クエリ文字列を付けて、どのキャンペーンからユーザーが来たのか追跡したいのは理解できる。その場合は送り手と受け手が協力関係にある。だがここでは、送り手が何の理由もなく何かを付け加えている。なぜだろう?

    • 元のサイト側から見れば マーケティング だ。著者が ref クエリ文字列から xyz.com からかなりのトラフィックが来ていると見て、そのサイトに広告を出したり提携したりできるかもしれない、という流れになる。
      正直、ニッチ/スタートアップ系サイトではかなり有用だ。Web 分析でこうした値を見て始まった対話の両側を経験したことがあり、一度は流入トラフィックを見て自分から連絡し、別の一度は自分がリンクしたサイトから連絡を受けた。どちらも相互に利益のあるパートナーシップに終わった。
      プライバシー上の論点もある程度は理解できるが、標準の Referer ヘッダー以上の情報を与えるわけではない。Simple Analytics や Plausible のような分析ツールを使うと、ずっと目立って見えるだけだ。
    • 概して追跡には反対だ。追跡はたいてい個人の利益に反する。
      クエリ文字列の追加はしばしば追跡に使われる。Firefox の “copy clean link” や、一部の UTM パラメータを事前に除去する Enhanced Tracking Protection のような機能が存在すること自体、多くの人がこれを望んでいない証拠だ。
      ある種のサイトは、私が軽く「追跡経済」と呼ぶ仕組みに喜んで参加している。受信側がログを見て、多くの人がそのサイトから来ていると分かり、自分にとって有益な行動を取れるからだ。
      クエリ文字列を拒否するのは、その仕組みに対するシンプルな抗議だ。
    • 人気のある Web サイトがそのパラメータを付ければ、対象サイトは誰がトラフィックを送っているのか簡単に把握でき、それが スポンサーシップ や提携契約の基盤になり得る。
  • 「Web サイト訪問者が、独立した個人 Web サイト運営者コミュニティが推薦する興味深い Web サイトやページを探索できるようにする、小さく分散型でセルフホスト可能な Web コンソール」という説明を見ると、昔はこういうものを Webring と呼んでいた。ただ、そこまで華やかではなかった。
    オープンソースのアプリケーションフレームワークを開発していたときに直面した問題の一つは、FastCGI を使うホスティングが Auth ヘッダーを尊重しないため、トークンをクエリで渡すしかなかったことだ。Web アドレスをコピー&ペーストするときにトークンが含まれてしまうことが多く、本当にひどかった。今では直っているかもしれない。
    自分で制御でき、全員に公開する必要のないバックエンドではヘッダーを使う。

    • オープンソースのアプリケーションフレームワークを fcgi-app で書いたということは、たとえば Apache が Auth ヘッダーを壊していた、という意味だろうか?
      この部分をもう少し詳しく説明してもらえるとありがたい。技術的には、PARAM レコードが期待した値を実際には渡していない、という話に聞こえる。
  • 「だからこのサイトでは包括的な禁止を試すことにした。承認されていないクエリ文字列は禁止だ」と言っているが、そのサイトはリクエストにクエリ文字列が含まれると 414 を返しているようで、私には誤った選択に思える。
    この抗議がユーザーを擁護するためのものなら、そもそもその文字列を制御できなかったはずのユーザーをなぜ罰するのだろうか?
    むしろ、ブラウザツールなどを通じてユーザー自身がこの判断を下せる方法を示すシグナルとして使う方がよいのではないか?

    • 「私が 414 URI Too Long を誤用していると言うことはできるだろう。私の答えは、こちらの方がより面白い、というものだ。検討した他の選択肢は次のとおりだ。
      400 Bad Request、一般的なクライアントエラーコードとしては適切だが面白くない。
      402 Payment Required、正直なところ、クエリ文字列付きの特定の URL を動くようにしてほしいと金を払うなら検討の余地はある。
      404 Not Found、ただし副作用が多く生じやすく、私の意図する『リクエストの形式が間違っている』という感じを伝えられない。
      Location ヘッダーなしの 303 See Other。今では極めて珍しいが正当だ。少なくとも RFC 2616 ではそうだった("The different URI SHOULD be given by the Location field in the response")。だが 7231 と 9110 では、Location ヘッダーの存在を前提とする形に変わった("… as indicated by a URI in the Location header field")。一方で 301、302、307、308 は "the server SHOULD generate a Location header field" となっている。いずれにせよ、Location ヘッダーなしの See Other も十分ありだと思う。でも URI Too Long の方が面白かった」
      https://chrismorgan.info/no-query-strings?foo
    • かなり昔のことなので記憶が曖昧だが、不明なクエリ文字列を渡すと 500 を返す PLSQL サーバーページのバージョンがあった気がする。
  • 「私が 414 URI Too Long を誤用していると言うことはできるだろう。私の答えは、こちらの方がより面白い、というものだ。検討した他の選択肢は…」というくだりでは、別の候補として 418 I'm a teapot も考えられる。ティーポットも通常はクエリ文字列をサポートしないからだ。

    • 単に 400 “Bad Request” や 403 “Forbidden” でも十分に擁護可能な選択に見える。URI パラメータ専用のエラー応答コードがないのは妙だ。
      適切に見えて、詳しく見るとそうでもない選択肢もいくつかある。406 “Not Acceptable” はコンテンツネゴシエーションヘッダーに基づくものだし、409 “Conflict” は主に WebDAV リクエスト向けで、411、422、431 などもここには関係のない特定条件用だ。
      300 番台や 500 番台のエラーも不適切だ。これはリダイレクトでもサーバー側障害でもなく、クライアント側のリクエストの問題だからだ。
      ティーポットか、長すぎる URI が最有力候補に思える。
    • もちろんサポートしている。たとえば上から糸を垂らしてティーポットの水位を問い合わせられるし、糸をポットの周囲に巻き付けて周囲長を問い合わせることもできる。
    • でも私はティーポットではない。お茶は嫌いだ。
  • この記事と Chris の記事の文体を見ると、こうしたクエリパラメータを含めることが有害であるかのように感じられるが、どう有害なのか分からない。
    一部の URL を壊し得ることは理解できるし、それだけでもやめる理由としては十分だ。それでも些細な不便に見える。誰か説明してくれないだろうか?

    • 三つの観点がある。
      技術的純粋主義の観点では、慣習として受け入れられていても、URL を改変するのは技術的に不正確だ。URL は基本的に 不透明な値 として扱うべきだ。
      社会的観点では追跡であり、その点は兄弟コメントスレッドでうまく説明されているので繰り返さない。
      ノイズの観点では、ユーザーが気にすべき部分を隠し、URL を過度に難解かつ複雑にして、一般の人々が URL に関心を持たなくなる一因になっている。
    • HTTP Referer ヘッダーに関する問題を読むと、人々がなぜ嫌うのか分かる。 https://en.wikipedia.org/wiki/HTTP_referer
      あるサイトに到達する前にどこにいたかを、そのサイトに知られたくない理由は多い。要するに、訪問先のサイトに 閲覧履歴 を共有しているようなものだ。
      そのため HTTP Referer ヘッダーには、送信条件の制限や機能全体を無効化できる仕組みなど、多くの更新が入ってきた。
      同じ情報を URL パラメータとして追加すると、こうした既存ルールや拒否機能を回避してしまう。素直に標準を使うべきだ。
    • まったく理由はない。その情報はただ捨てればよい。
      ひどく極端な態度で、これがどうしてより良い Web につながるのか十分に説明できていない。
    • 興味深いのは、こうしたサイトの中に 検索機能 を持つものがないことだ。検索は重要なアクセシビリティ機能であり、クエリ文字列の明確で正当なユースケースだ。
    • 理由はいくつかある。ユーザーは追跡に同意しておらず、この種のクエリパラメータは 追跡情報 だ。また、サイト管理者が流入トラフィックの追跡を望まない場合もある。
      後者は理解しにくいかもしれないが、私の場合、ログにユーザーを害し得る情報が残ることを絶対に望まない。
      個人的には、リンクをコピーしてメッセージで送ろうとしたときに、元の URL の倍もある追跡コードが付いていると本当にうんざりする。いちいち消さなければならないし、そうでなければ画面いっぱいのランダムな文字列を受け取った相手が、いったい何なのかと戸惑うことになる。
      ユーザープライバシーを侵害し、ユーザー体験も悪く、何より誰も頼んでいない。
  • 元記事のソースはまだ HN で議論されたことがなかったので、そのリンク(https://chrismorgan.info/no-query-strings)を一番上に置き、返信記事へのリンク(https://susam.net/no-query-strings.html)は上部の説明に移した。
    どちらも良いが、元記事を優先する方が公平に思える。

  • このあたりでまだ GET クエリを使っているサイトの大半は、地方自治体が運営する 税金徴収サイト で、ログイン後に変数をあちこち受け渡している。
    実際には GET リクエストと同じことをしているのに、本物の URL のふりをするルーティングパーサの方がずっと腹立たしい。

  • クエリ文字列は有用だ。ファイル検索や、ほかの種類の動的ファイルなどがそうだが、クエリ文字列を想定していない URL に付けるべきではない。
    だから UTM などが追加されたリクエストを拒否するのは正しいと思う。
    クエリ文字列が想定されていないのに存在するなら、応答としては 404 が最もしっくりくる気がするし、400 でも適切かもしれない。