@prefix sioc: <http://rdfs.org/sioc/ns#> .
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix content: <http://purl.org/rss/1.0/modules/content/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://lhero.org/?post_type=lh-portfolio-doc&#038;p=150148>
  a sioc:Post ;
  dc:title "Preserved Query Strings and Hooks" ;
  dcterms:identifier 150148 ;
  dc:modified "2026-10-13T13:50:12Z"^^xsd:dateTime ;
  dc:created "2026-10-13T13:30:10Z"^^xsd:dateTime ;
  sioc:link <https://lhero.org/portfolio/lh-canonicaliser/preserved-query-strings-and-hooks/> ;
  sioc:has_creator <https://lhero.org/author/1/#account> ;
  sioc:has_container <https://lhero.org/#posts> ;
  content:encoded """<ul class="lh_portfolio-meta"><li><strong>Part of:</strong> <a href="https://lhero.org/portfolio/lh-canonicaliser/">LH Canonicaliser</a></li></ul>
<p class="wp-block-paragraph">The address bar URL is built in PHP and passed to the script, which only checks it is on the same origin and calls <code>history.replaceState()</code>. The <code>rel="canonical"</code> link in the head is never changed.</p>



<ol class="wp-block-list"><li><strong>Canonical URL:</strong> <code>bp_get_canonical_url()</code> on BuddyPress pages, <code>wp_get_canonical_url()</code> on single posts and pages, otherwise nothing. Any other page can supply one with the <code>lh_canonicaliser_canonical_url</code> filter; LH SEO Meta Tags uses it for a blog-index front page. With no canonical URL the script is not loaded.</li><li><strong>Preserved query strings:</strong> every preserved key present on the current request is added to the canonical URL, unless the canonical URL already has that key. Any other query string is dropped.</li><li><strong>Fragment and state:</strong> the script keeps the current <code>#fragment</code> and <code>history.state</code>, and does nothing if the URL already matches.</li></ol>



<h3 class="wp-block-heading">Preserved by default</h3>



<figure class="wp-block-table"><table><thead><tr><th>Keys</th><th>Why</th></tr></thead><tbody><tr><td><code>s</code>, <code>search</code>, <code>members_search</code>, <code>groups_search</code></td><td>Search terms (WordPress and BuddyPress)</td></tr><tr><td><code>redirect_to</code></td><td>Where to go after logging in</td></tr><tr><td><code>replytocom</code></td><td>Opens the comment reply form when JavaScript comment replies are unavailable</td></tr><tr><td><code>unapproved</code>, <code>moderation-hash</code></td><td>Lets a commenter see their own comment while it awaits moderation</td></tr><tr><td><code>preview</code>, <code>preview_id</code>, <code>preview_nonce</code></td><td>Post previews</td></tr><tr><td><code>_wpnonce</code>, <code>action</code>, <code>key</code>, <code>login</code></td><td>Action, confirmation and password reset links</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">Filters</h3>



<ul class="wp-block-list"><li><code>lh_canonicaliser_preserved_query_vars</code> (array of keys): add or remove preserved query strings. Matching is exact and case-sensitive.</li><li><code>lh_canonicaliser_preserved_args</code> (array of key =&gt; value): the preserved values found on the current request, before they are added to the URL.</li><li><code>lh_canonicaliser_canonical_url</code> (string|false): the canonical URL for the current page. Return a URL for pages core and BuddyPress do not cover, or false to leave a page alone.</li><li><code>lh_canonicaliser_maybe_enqueue</code> (bool): return false to keep the script off the current request.</li><li><code>lh_canonicaliser_init_plugin</code> (bool): return false to stop the plugin hooking in at all.</li></ul>



<pre class="wp-block-code"><code>add_filter( 'lh_canonicaliser_preserved_query_vars', function ( $keys ) {
	$keys[] = 'lang';
	return $keys;
} );</code></pre>



<h3 class="wp-block-heading">When the script loads</h3>



<p class="wp-block-paragraph">It is enqueued on <code>wp_enqueue_scripts</code>, deferred in the footer, so it works with block and classic themes and never runs on <code>wp-login.php</code> or in wp-admin. It is skipped on non-GET requests, embeds, previews, Customizer previews, feeds and <code>robots.txt</code>. The target URL reaches the script as <code>window.lhCanonicaliser.url</code>, printed inline before it.</p>



<h3 class="wp-block-heading">Page caching</h3>



<p class="wp-block-paragraph">Preserved values are written into the page HTML. A key added through the filter must also be one the page cache varies on, or a cached page can show another visitor&#8217;s value in the address bar. The defaults are all keys that vary the cache or stop the page being cached.</p>
"""^^rdf:XMLLiteral ;
  sioc:content """Part of: LH Canonicaliser
The address bar URL is built in PHP and passed to the script, which only checks it is on the same origin and calls history.replaceState(). The rel="canonical" link in the head is never changed.



Canonical URL: bp_get_canonical_url() on BuddyPress pages, wp_get_canonical_url() on single posts and pages, otherwise nothing. Any other page can supply one with the lh_canonicaliser_canonical_url filter; LH SEO Meta Tags uses it for a blog-index front page. With no canonical URL the script is not loaded.Preserved query strings: every preserved key present on the current request is added to the canonical URL, unless the canonical URL already has that key. Any other query string is dropped.Fragment and state: the script keeps the current #fragment and history.state, and does nothing if the URL already matches.



Preserved by default



KeysWhys, search, members_search, groups_searchSearch terms (WordPress and BuddyPress)redirect_toWhere to go after logging inreplytocomOpens the comment reply form when JavaScript comment replies are unavailableunapproved, moderation-hashLets a commenter see their own comment while it awaits moderationpreview, preview_id, preview_noncePost previews_wpnonce, action, key, loginAction, confirmation and password reset links



Filters



lh_canonicaliser_preserved_query_vars (array of keys): add or remove preserved query strings. Matching is exact and case-sensitive.lh_canonicaliser_preserved_args (array of key =&gt; value): the preserved values found on the current request, before they are added to the URL.lh_canonicaliser_canonical_url (string|false): the canonical URL for the current page. Return a URL for pages core and BuddyPress do not cover, or false to leave a page alone.lh_canonicaliser_maybe_enqueue (bool): return false to keep the script off the current request.lh_canonicaliser_init_plugin (bool): return false to stop the plugin hooking in at all.



add_filter( 'lh_canonicaliser_preserved_query_vars', function ( $keys ) {
	$keys[] = 'lang';
	return $keys;
} );



When the script loads



It is enqueued on wp_enqueue_scripts, deferred in the footer, so it works with block and classic themes and never runs on wp-login.php or in wp-admin. It is skipped on non-GET requests, embeds, previews, Customizer previews, feeds and robots.txt. The target URL reaches the script as window.lhCanonicaliser.url, printed inline before it.



Page caching



Preserved values are written into the page HTML. A key added through the filter must also be one the page cache varies on, or a cached page can show another visitor&#8217;s value in the address bar. The defaults are all keys that vary the cache or stop the page being cached.
""" ;
  sioc:topic <https://lhero.org/?taxonomy=author&term=cap-1> .

<https://lhero.org/author/1/#account> rdfs:seeAlso <https://lhero.org/author/1/?feed=lhrdf&format=turtle> .
<https://lhero.org/?taxonomy=author&term=cap-1> rdfs:seeAlso <https://lhero.org/?taxonomy=author&term=cap-1&feed=lhrdf&format=turtle> .
