Key points
- In 2013 I encrypted my blog’s outgoing links, so a copied article kept pointing back here and its links broke for the copier.
- It no longer works as written: WordPress.org closed the plugin, and the
mcryptextension the code used was removed from PHP. - An OpenSSL (AES-256-GCM) replacement for the two encryption lines is under the code, for PHP 7.1 and newer.
Encrypting WordPress External Links on Your Blog
Updated October 2026: This trick for encrypting WordPress external links no longer works as written, so treat the steps below as history:
- WordPress.org closed the WP No External Links plugin on 19 December 2017 for a security issue, and it can no longer be downloaded.
- The mcrypt extension the code needs was deprecated in PHP 7.1 and removed in PHP 7.2. No supported Ubuntu release ships php5-mcrypt, and CentOS Linux reached end of life in June 2024.
- MCRYPT_RIJNDAEL_256 was never AES (it uses a 256-bit block), and ECB mode leaks patterns, so even on old servers this was weak encryption.
- If you still want opaque outbound links, use a maintained link plugin and do the encryption with OpenSSL. Under the instructions I added a PHP 7.1 and PHP 8 version of my two replacement lines that uses AES-256-GCM and rejects tampered links.
As you may notice, all external links on my blog used to be encrypted to protect the content from being copied. I tried to find a plugin that does this, but I couldn’t find one, so I modified one that encodes external URLs to Base64. Instead of encoding the URL, I will show you how to encrypt it with your defined key. I will show you how I did it through simple steps if you wish to have external links encrypted on your WordPress blog.
The plugin author merged my code into his own from version 4.2.0, so version 4.2.0 and later did not need my code.
Instructions
# You will need php mcrypt_decrypt installed on your server
# For CentOS just type
yum install php-mcrypt
# For ubuntu just type
sudo apt-get install php5-mcrypt
// Install WP No External Links plugin I am currently using version 4.0.2.
// Activate the plugin.
// Go to setting then wp-noexternallinks select the check box of Use base64 encoding...etc.
// Now go to plugin editor.
// Versions below 4.0 Select WP No External Links then select wp-noexternallinks-parser.php
// Versions above 4.0 Select WP No External Links then select public then public.php
// Search for:
$url=base64_encode($url);
// Replace it with:
$salt ='PSOtJIaJCSRthDJ3ZkcNZVubMYZOZZrn'; // <--- This is your secret key it can be anything you like remember only keys of sizes 16, 24 or 32 supported in mcrypt_encrypt.
$url=trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $url, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
// Then Search for:
$url=base64_decode($url);
// Replace it with:
$salt ='PSOtJIaJCSRthDJ3ZkcNZVubMYZOZZrn'; // <--- We use the same key to decrypt
$url=trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($url), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));PHPFor PHP 7.1 and newer (PHP 8 included), mcrypt is gone; this OpenSSL version replaces the two mcrypt lines above, uses AES-256-GCM, and makes a tampered link decrypt to false:
// PHP 7.1 or newer (PHP 8 included): mcrypt was removed in PHP 7.2, so OpenSSL does the job.
// Use the same secret in both places; change it to make every old link invalid.
// Replace $url=base64_encode($url); with:
$key = hash('sha256', 'your-own-long-random-secret', true);
$iv = random_bytes(12);
$enc = openssl_encrypt($url, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$url = rtrim(strtr(base64_encode($iv . $tag . $enc), '+/', '-_'), '=');
// Replace $url=base64_decode($url); with:
$key = hash('sha256', 'your-own-long-random-secret', true);
$raw = (string) base64_decode(strtr($url, '-_', '+/'));
$url = strlen($raw) > 28 ? openssl_decrypt(substr($raw, 28), 'aes-256-gcm', $key, OPENSSL_RAW_DATA, substr($raw, 0, 12), substr($raw, 12, 16)) : false;
The reason for using a fixed $salt key (it is really an encryption key, not a salt) is that, if your content gets copied, you can change the key in both locations and all of the previous links will become invalid for the person who copied your article.
Troubleshoot
Updated October 2026: An old editor update removed the HTML tags inside some of these PHP strings (the <a ...> part of the two $pattern lines, the <span class="waslinkname"> and </span> around $matches, and the closing </a> of $link). I restored them in October 2026 from the plugin file in the download, and both downloads now use the OpenSSL code above instead of mcrypt, with a placeholder secret for you to change.
I faced some difficulties with some of the social share links (LinkedIn, Pinterest, mailto, Skype and local links starting with #). Though the author of the plugin did exclude those strings, it did not work as expected, so I had to tweak the code as follows:
// Search for:
$this->debug_info('Not in exclusion list, masking...');
// Add just before it:
if (false !== strpos($matches[2] . '//' . $matches[3],'javascript') or false !== strpos($matches[2] . '//' . $matches[3],'mailto') or false !== strpos($matches[2] . '//' . $matches[3],'twitt') or false !== strpos($matches[2] . '//' . $matches[3],'skype') or false !== strpos($matches[2] . '//' . $matches[3],'#')) {
return $matches[0];
}
// If you get your page and comments section messed up you will need to replace:
$pattern = '/<a (.*?)href=[\"\'](.*?)\/\/(.*?)[\"\'](.*?)>(.*?)<\/a>/si';
// with:
$pattern = '/<a (.*?)href=[\"\'](.*?)\/\/(.*?)[\"\'](.*?)>(.*?)<\/a>/i';
// If all links are labelled as _blank you have to add:
$url=$matches[2].'//' .$matches[3];
// After:
$ifnofollow=' rel="nofollow"';
// Then replace:
return '<span class="waslinkname">'.$matches[4].'</span>';
if($this->options['link2text'])
return '<span class="waslinkname">'.$matches[4].'</span> ^(<span class="waslinkurl">'.$url.')</span>';
$link='<a'.$ifblank.$ifnofollow.' href="'.$url.'" '.$matches[1].$matches[3].'>'.$matches[4].'</a>';
// with:
return '<span class="waslinkname">'.$matches[5].'</span>';
if($this->options['link2text'])
return '<span class="waslinkname">'.$matches[5].'</span> ^(<span class="waslinkurl">'.$url.')</span>';
$link='<a'.$ifblank.$ifnofollow.' href="'.$url.'" '.$matches[1].$matches[4].'>'.$matches[5].'</a>';PHPOutput examples
// Normal URL to Hushmail
https://www.hushmail.com
// With WP No External Links plugin (base64)
http://www.digi77.com/goto/aHR0cHM6Ly93d3cuaHVzaG1haWwuY29t
// With WP No External Links plugin + Encryption
http://www.digi77.com/goto/HkiabvdkLqUt0uihqCaFE0tXqo1T4CccxSSjvehwcu0=PHP