skip to Main Content

Link obfuscation is a more and more common topic in order to improve SEO by masking the non-important links to provide more weight to others.

I’m looking for an efficient way to obfuscate links in WordPress, directly in the source code obviously, for example by adding a special class to my links.

It must turn the <a> element into something else like a <span>, with no more visible href attribute nor any actual URL, so that robots cannot see any link.

It must be done before rendering the source code, not overridden in JavaScript.

Example :

<a href="https://example.com">Hello</a>

turns into :

<span data-o="CRYPTEDLINK">Hello</span>

then some JS allows click on the element to redirect to the original link.

2

Answers


  1. Chosen as BEST ANSWER

    I ended up making my own system that allows me to obfuscate any link easily.

    Add the following code to your child theme's functions.php file, then just add the class "obfuscate" to any element to obfuscate its link by replacing it with a element with no readable link.

    Also be sure to edit styles above, or delet them and style the "akn-obf-link" class in your own CSS file, so that it looks like a link to the visitor.

    <?php
    
    /**************************************************************************************
    |* Links obfuscation - add class "obfuscate" to any <a> element to obfuscate its link *|
    **************************************************************************************/
    
    // Add this code to your child theme's functions.php file, then just add the class "obfuscate" to any <a> element to obfuscate its link by replacing it with a <span> element with no readable link.
    // The obfuscated elements inherits the original <a> element's classes, along with a "akn-obf-link" class, so you might need to add CSS to style the "akn-obf-link" class so that it looks like a link to the visitor, maybe at least to add a cursor:pointer.
    // On right click, the obfuscated link will be wrapped with a proper <a> element with the "akn-deobf-link" for a brief moment, so that a proper context menu appears, you can remove that behaviour by setting the "deobfuscate_on_right_click" option to false in the code bellow.
    
    // Edit 2022-04-05 - modified regex to allow for html elements and new lines into the <a> element, modified callback so the obfuscated element inherits the original link's classes, modified JS to add mousewheel click and right click options.
    
    // Edit 2023-01-26 - greatly improved regex thanks to @MadMaxInfinity on Stack Overflow, it now both allows more matches in different scenarios and returns less false positives matches, more infos on his answer here: https://stackoverflow.com/a/75234749/2498324
    
    // Edit 2023-02-08 - improved class regex thanks to @MadMaxInfinity on Stack Overflow again.
    
    add_action('wp_loaded', 'buffer_start');
    function buffer_start() {
        ob_start('akn_ofbuscate_buffer');
    }
    add_action('shutdown', 'buffer_end');
    function buffer_end() {
        ob_end_flush();
    }
    function akn_ofbuscate_buffer($buffer) {
        $result = preg_replace_callback('#<a[^>]+((?<=s)href=("|')([^"']*)('|")[^>]+(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')|(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')[^>]+(?<=s)href=("|')([^"']*)('|"))[^>]*>(.*)</a>#miUs', function($matches) {
            preg_match('#<a[^>]+(?<=s)class=["|\']([^\'"]+)["|\']#imUs',$matches[0],$matches_classes);
            $classes = trim(preg_replace('/s+/',' ',str_replace('obfuscate','',$matches_classes[1])));
            return '<span class="akn-obf-link'.($classes?' '.$classes:'').'" data-o="'.base64_encode($matches[3]?:$matches[10]).'" data-b="'.((strpos(strtolower($matches[0]),'_blank')!==false)?'1':'0').'">'.$matches[12].'</span>';
        }, $buffer);
        return $result;
    }
    add_action('wp_footer', 'akn_ofbuscate_footer_js');
    function akn_ofbuscate_footer_js() {
        ?>
            <script>
                jQuery(document).ready(function($) {
                    // options you can change
                    var deobfuscate_on_right_click = true;
                    // function to open link on click
                    function akn_ofbuscate_clicked($el,force_blank) {
                        if (typeof(force_blank)=='undefined')
                            var force_blank = false;
                        var link = atob($el.data('o'));
                        var _blank = $el.data('b');
                        if (_blank || force_blank)
                            window.open(link);
                        else
                            location.href = link;
                    }
                    // trigger link opening on click
                    $(document).on('click','.akn-obf-link',function() {
                        var $el = $(this);
                        if (!$el.closest('.akn-deobf-link').length)
                            akn_ofbuscate_clicked($el);
                    });
                    // trigger link openin in new tab on mousewheel click
                    $(document).on('mousedown','.akn-obf-link',function(e) {
                        if (e.which==2) {
                            var $el = $(this);
                            if (!$el.closest('.akn-deobf-link').length) {
                                akn_ofbuscate_clicked($el,true);
                                return true;
                            }
                        }
                    });
                    // deobfuscate link on right click so the context menu is a legit menu with link options
                    $(document).on('contextmenu','.akn-obf-link',function(e) {
                        if (deobfuscate_on_right_click) {
                            var $el = $(this);
                            if (!$el.closest('.akn-deobf-link').length) {
                                e.stopPropagation();
                                var link = atob($el.data('o'));
                                var _blank = $el.data('b');
                                $el.wrap('<a class="akn-deobf-link" href="'+link+'"'+(_blank?' target="_BLANK"':'')+'></a>').parent().trigger('contextmenu');
                                setTimeout(function() {
                                    $el.unwrap();
                                },10);
                            }
                        }
                    });
                });
            </script>
        <?php
    }
    

    I'm also sharing the code on this Pastebin: https://pastebin.com/cXEBSVFn

    Consider checking the link just in case I updated the code on it and forgot to update it here


  2. I suggest to modify the "detection" regular expression used with preg_replace_callback.

    First of all, you may add a question mark right after the group between the tags as a link having no text is valid according to W3C validator i.e. <a href=...></a>.

    A second suggestion is to add (?<!w|-) before and (?!w|-) after the class name to detect. Otherwise, you get false detections with class names like do-not-obfuscate_this or notobfuscated.

    My third suggestion is to add (?<=s) before each href and class word. To avoid matching custom attributes like data-href= or unclassify=.

    My last suggestion is to remove (?!<a) from the end as the expression is non greedy (and nesting <a> tags -the idea between this?- is not allowed). Thus, (.+(?!<a))</a> should become (.+)</a>. And this, as it should be combined to the suggestion one, should lead us to (.*)</a> (no need for (.+)?</a>).

    Finaly, the regular expression I use is:

    '#<a[^>]+((?<=s)href=("|')([^"']*)('|")[^>]+(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')|(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')[^>]+(?<=s)href=("|')([^"']*)('|"))[^>]*>(.*)</a>#miUs'

    You may be interested in checking the differences between your regexp and mine (check the unit tests).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search