Rob Severijns Posted October 19, 2025 Report Share Posted October 19, 2025 I know I'm not a security expert but still hope it's helpfull to some of you. If anyone likes to chime in, feel free to do so. About the topic In TNG you can use the "rel" attribute with hyperlinks in order to add extra security & privacy. See the table below for an explanation of the "rel" attribute or search the internet for more detailed information. Unfortunatly the "rel" attribute can't be used with hyperlinks in citations. To mitigate this you can use the JavaScript code at the end of this topic. What does it do: Detects external links only (not your own domain) Forces them to open in a new tab (target="_blank") Automatically adds secure attributes: rel="noopener noreferrer nofollow" Works for dynamically added links (AJAX, React, etc.) Which gives you: SEO-safe: external links use nofollow Security-safe: prevents tab-nabbing with noopener noreferrer User-friendly: external links open in new tabs Robust: updates automatically as new links appear Just add the script at the bottom of the footer.php in your template folder. BTW TNG doesn't always have the closing </body> tag in footer.php but that fine as long as the script is placed at the bottom of the footer.php it works. If you remove the "nofollow" value from the script you allow search engines to to use the link as a ranking signal. Simply said: Skipping nofollow won’t break anything. It just means you’re passing SEO value to the linked page. Only use nofollow when you don’t fully trust or control that link. The script is shown below. <!-- Add this just before the closing </body> tag --> <script> (() => { const addSafeExternalAttrs = link => { const href = link.getAttribute('href'); if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return; const linkUrl = new URL(href, window.location.origin); if (linkUrl.origin === window.location.origin) return; link.setAttribute('target', '_blank'); const existingRel = link.getAttribute('rel') || ''; const relValues = new Set(existingRel.split(/\s+/).filter(Boolean)); ['noopener', 'noreferrer', 'nofollow'].forEach(v => relValues.add(v)); link.setAttribute('rel', Array.from(relValues).join(' ')); }; document.querySelectorAll('a[href]').forEach(addSafeExternalAttrs); const observer = new MutationObserver(mutations => { for (const mutation of mutations) { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (node.tagName === 'A') addSafeExternalAttrs(node); else node.querySelectorAll?.('a[href]').forEach(addSafeExternalAttrs); } }); } }); observer.observe(document.body, { childList: true, subtree: true }); })(); </script> To test if the script works you can do the following: Go to a page with an external hyperlink Rightclick the hyperlink and choose "Inspect" You should see something like this: <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">Example</a> If those attributes were missing before but now appear automatically the script works. If they don’t appear, clear the cache or hard refresh with Ctrl+F5 or otherwise check the console for errors (red text) Quote Link to comment Share on other sites More sharing options...
Michel KIRSCH Posted October 21, 2025 Report Share Posted October 21, 2025 Hi Rob, trying the code with insert it into the end.php file. It works as attended. Michel Quote Link to comment Share on other sites More sharing options...
Rob Severijns Posted October 21, 2025 Author Report Share Posted October 21, 2025 Hi Michel, Thank you for testing and replying. Is there a specific reason why you entered the JS script in end.php instead of footer.php? Both seem to work Quote Link to comment Share on other sites More sharing options...
Rob Severijns Posted October 21, 2025 Author Report Share Posted October 21, 2025 Hi Michel, Did a quick follow up check and it seems that footer.php is the better option since it loads with every page. end.php can be used if end.php is called for at the end of every page. Not sure if that's the case with TNG Let me know if my thoughts are correct or if it needs further explanation. Quote Link to comment Share on other sites More sharing options...
Michel KIRSCH Posted October 21, 2025 Report Share Posted October 21, 2025 Hi Rob, I just do a quick test with getperson.php. And it seems that end.php is loaded by genlib.php. ($flags['noend'] is never set) I didn't look for admin pages... Michel Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.