How to add query parameters to all internal links?

The following is not a complete solution (i.e., as @cjbj mentioned, it won’t handle links in post content), but it will achieve what you want for any link whose URL is obtained (in WP Core, a theme or a plugin) via get_permalink() (including the output of wp_page_menu(), and kin).

$filters = array (
    'post_link',       // when post_type == 'post'
    'page_link',       // when post_type == 'page'
    'attachment_link', // when post_type == 'attachment'
    'post_type_link',  // when post_type is not one of the above
    ) ;
foreach ($filters as $filter) {
    add_filter ($filter, 'wpse_add_current_requests_query_args', 10, 3) ;
    }

function
wpse_add_current_requests_query_args ($permalink, $post, $leavename)
{
    if (!is_admin ()) {
        // we only want to modify the permalink URL on the front-end
        return ;
        }

    // for the purposes of this answer, we ignore the $post & $leavename
    // params, but they are there in case you want to do conditional
    // processing based on their value
    return (esc_url (add_query_arg ($_GET, $permalink))) ;
}

Explanation

Before returning it’s result, get_permalink() applies one of 4 filters (named in the $filters array above) on the permalink it has generated. So, we hook into each of those filters.

The function we hook to these filters calls add_query_arg() to add any query args present in the current request (i.e., $_GET).

Important Security Consideration

As mentioned in add_query_arg():

Important: The return value of add_query_arg() is not escaped by default. Output should be late-escaped with esc_url() or similar to help prevent vulnerability to cross-site scripting (XSS) attacks.

Calling esc_url() in wpse_add_current_requests_query_args() is not what I would call “late-escaped” in all cases. But unfortunately, a number of WP Core funcs (e.g. Walker_Page::start_el(), which is ultimately called by wp_page_menu()), don’t call esc_url() on the return value of get_permalink(), so we have to call it in our filter hook to be safe.

Leave a Comment