543c9ee045ba1832faaa9b75c6dc1e369dffce5a
max
  Thu Sep 10 05:21:42 2026 -0700
Login and Sign out come back to a page that was reached by POST, refs #38192

Clicking a track name in the list below the browser image submits the
hgTracks form to hgTrackUi, so the request is a POST even though the
track name sits in the URL. The return URL builder threw the query
string away for anything that was not a GET, which left a returnto of
hgTrackUi?hgsid= alone, and hgTrackUi cannot draw a page from that
because the track name is deliberately not kept in the cart. Login and
Sign out therefore ended in an error instead of coming back.

The query string of a POST lives in the form's action URL, which is the
address the browser is showing, so returning to it is no different from
the visitor pressing reload. Only the form body is left behind, and the
cart already holds what mattered from it. hgTracks stays the exception:
its query string can hold a one-shot zoom or drag.

Also, hgTrackUi now says which parameter is missing when it is reached
without a track name, rather than failing on a bare hash lookup, and
hgCollection's own "you must be logged in" link brings the visitor back
to hgCollection instead of the sessions page.

diff --git src/hg/lib/wikiLink.c src/hg/lib/wikiLink.c
index b7df763b661..218199ba3c3 100644
--- src/hg/lib/wikiLink.c
+++ src/hg/lib/wikiLink.c
@@ -538,38 +538,40 @@
  * to its own default.  Free when done. */
 {
 char *scriptName = cgiScriptName();
 if (isEmpty(scriptName))
     return NULL;
 char *lastSlash = strrchr(scriptName, '/');
 char *cgiName = (lastSlash == NULL) ? scriptName : lastSlash + 1;
 if (isEmpty(cgiName))
     return NULL;
 // hgLogin is where the link points, so returning to it would only loop.  hgRenderTracks just
 // draws an image for another website, it is not a page anyone is sitting on.
 if (sameString(cgiName, "hgLogin") || sameString(cgiName, "hgRenderTracks"))
     return NULL;
 
 /* The query string is what makes a page like hgTrackUi or hgc work at all, since their track
- * and item parameters are not all kept in the cart.  Coming back to a URL the visitor clicked
- * themselves does no more than their reload button would, as long as it was a GET; a POST
- * cannot be replayed from a URL anyway.  hgTracks is the exception: everything it needs is in
- * the cart, and its query string can hold a one-shot zoom or drag that we do not want to
- * repeat. */
+ * and item parameters are not all kept in the cart.  Coming back to that URL does no more than
+ * the visitor's own reload button would, and that holds for a POST as well: the query string
+ * of a POST sits in the form's action URL, which is the address the browser is showing, so
+ * only the form body is left behind and the cart already has what mattered from it.  Dropping
+ * the query string here used to send the track settings page back to hgTrackUi with nothing
+ * but an hgsid, which cannot work, since the track name is deliberately kept out of the cart.
+ * hgTracks is the exception: everything it needs is in the cart, and its query string can hold
+ * a one-shot zoom or drag that we do not want to repeat. */
 char *queryString = getenv("QUERY_STRING");
-char *method = cgiRequestMethod();
-if (sameString(cgiName, "hgTracks") || (method != NULL && differentWord(method, "GET")))
+if (sameString(cgiName, "hgTracks"))
     queryString = NULL;
 
 char *url = currentPageUrl(cgiName, hgsid, queryString);
 char *encoded = wikiLinkEncodePageReturnUrl(url);
 if (encoded == NULL && queryString != NULL)
     {
     // A stray quote or an over-long query string costs the query string, not the page
     freez(&url);
     url = currentPageUrl(cgiName, hgsid, NULL);
     encoded = wikiLinkEncodePageReturnUrl(url);
     }
 freez(&url);
 return encoded;
 }