Free PHP URL shortener script that kicks ass
Reading time: About 2 minutes
After seeing some of the other coding approaches to personal short URL services, I decided to write my own PHP URL shortener script and share it with the world.
Thanks to the help of others, such as Nicolas, Derek Gathright, and Adrien Gibrat I’ve been able to take this script much farther.
Benefits
- Can shorten over 42 billion unique URLs in 6 or less characters (it can do more than 12,000,000 in only 4!)
- Super duper fast—uses very little server resources
- Includes an API so you can create your own short URLs on the fly
- Option to turn clickthru tracking on and off
- Option to limit usage to 1 IP address for personal use and to prevent spamming from others
- Only uses alphanumeric characters so all browsers can interpret the URL
- Secure—several data filters in place to prevent SQL injection hacks
- Option to check if the URL is real (doesn’t respond with a 404) before shortening
- Uses 301 redirects for SEO and analytics yumminess
- Option to store a local cache to prevent database queries on every redirect
- Option to change the characters allowed in a shortened url
Installation
- Make sure your server meets the requirements:
- Optionally you can run this from your current domain or find a short domain
- Apache
- PHP
- MySQL
- Access to run SQL queries for installation
- Download a .zip file of the PHP URL shortener script files
- Upload the contents of the .zip file to your web server
- Update the database info in config.php
- Run the SQL included in shortenedurls.sql. Many people use phpMyAdmin for this, if you can’t do it yourself contact your host.
- Rename rename.htaccess to .htaccess
- If you want to use the caching option, create a directory named cache with permissions 777
Using your personal URL shortener service
To manually shorten URLs open in your web browser the location where you uploaded the files. You should see a form that looks and acts like this.
To programmatically shorten URLs with PHP use the following code:
$shortenedurl = file_get_contents('http://yourdomain.com/shorten.php?longurl=' . urlencode('http://' . $_SERVER['HTTP_HOST'] . '/' . $_SERVER['REQUEST_URI']));A look at the PHP code for curious programmers
Shorten.php – Script shortens URLs
<?php /* * First authored by Brian Cray * License: http://creativecommons.org/licenses/by/3.0/ * Contact the author at http://briancray.com/ */ ini_set('display_errors', 0); $url_to_shorten = get_magic_quotes_gpc() ? stripslashes(trim($_REQUEST['longurl'])) : trim($_REQUEST['longurl']); if(!empty($url_to_shorten) && preg_match('|^https?://|', $url_to_shorten)) { require('config.php'); // check if the client IP is allowed to shorten if($_SERVER['REMOTE_ADDR'] != LIMIT_TO_IP) { die('You are not allowed to shorten URLs with this service.'); } // check if the URL is valid if(CHECK_URL) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url_to_shorten); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); $response = curl_exec($ch); curl_close($handle); if(curl_getinfo($ch, CURLINFO_HTTP_CODE) == '404') { die('Not a valid URL'); } } // check if the URL has already been shortened $already_shortened = mysql_result(mysql_query('SELECT id FROM ' . DB_TABLE. ' WHERE long_url="' . mysql_real_escape_string($url_to_shorten) . '"'), 0, 0); if(!empty($already_shortened)) { // URL has already been shortened $shortened_url = getShortenedURLFromID($already_shortened); } else { // URL not in database, insert mysql_query('LOCK TABLES ' . DB_TABLE . ' WRITE;'); mysql_query('INSERT INTO ' . DB_TABLE . ' (long_url, created, creator) VALUES ("' . mysql_real_escape_string($url_to_shorten) . '", "' . time() . '", "' . mysql_real_escape_string($_SERVER['REMOTE_ADDR']) . '")'); $shortened_url = getShortenedURLFromID(mysql_insert_id()); mysql_query('UNLOCK TABLES'); } echo BASE_HREF . $shortened_url; } function getShortenedURLFromID ($integer, $base = ALLOWED_CHARS) { $length = strlen($base); while($integer > $length - 1) { $out = $base[fmod($integer, $length)] . $out; $integer = floor( $integer / $length ); } return $base[$integer] . $out; }Redirect.php – Script handles short url redirects
<?php /* * First authored by Brian Cray * License: http://creativecommons.org/licenses/by/3.0/ * Contact the author at http://briancray.com/ */ ini_set('display_errors', 0); if(!preg_match('|^[0-9a-zA-Z]{1,6}$|', $_GET['url'])) { die('That is not a valid short url'); } require('config.php'); $shortened_id = getIDFromShortenedURL($_GET['url']); if(CACHE) { $long_url = file_get_contents(CACHE_DIR . $shortened_id); if(empty($long_url) || !preg_match('|^https?://|', $long_url)) { $long_url = mysql_result(mysql_query('SELECT long_url FROM ' . DB_TABLE . ' WHERE id="' . mysql_real_escape_string($shortened_id) . '"'), 0, 0); @mkdir(CACHE_DIR, 0777); $handle = fopen(CACHE_DIR . $shortened_id, 'w+'); fwrite($handle, $long_url); fclose($handle); } } else { $long_url = mysql_result(mysql_query('SELECT long_url FROM ' . DB_TABLE . ' WHERE id="' . mysql_real_escape_string($shortened_id) . '"'), 0, 0); } if(TRACK) { mysql_query('UPDATE ' . DB_TABLE . ' SET referrals=referrals+1 WHERE id="' . mysql_real_escape_string($shortened_id) . '"'); } header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $long_url); exit; function getIDFromShortenedURL ($string, $base = ALLOWED_CHARS) { $length = strlen($base); $size = strlen($string) - 1; $string = str_split($string); $out = strpos($base, array_pop($string)); foreach($string as $i => $char) { $out += strpos($base, $char) * pow($length, $size - $i); } return $out; }config.php – Configures the URL shortener
<?php /* * First authored by Brian Cray * License: http://creativecommons.org/licenses/by/3.0/ * Contact the author at http://briancray.com/ */ // db options define('DB_NAME', 'your db name'); define('DB_USER', 'your db usernae'); define('DB_PASSWORD', 'your db password'); define('DB_HOST', 'localhost'); define('DB_TABLE', 'shortenedurls'); // connect to database mysql_connect(DB_HOST, DB_USER, DB_PASSWORD); mysql_select_db(DB_NAME); // base location of script (include trailing slash) define('BASE_HREF', 'http://' . $_SERVER['HTTP_HOST'] . '/'); // change to limit short url creation to a single IP define('LIMIT_TO_IP', $_SERVER['REMOTE_ADDR']); // change to TRUE to start tracking referrals define('TRACK', FALSE); // check if URL exists first define('CHECK_URL', FALSE); // change the shortened URL allowed characters define('ALLOWED_CHARS', '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); // do you want to cache? define('CACHE', TRUE); // if so, where will the cache files be stored? (include trailing slash) define('CACHE_DIR', dirname(__FILE__) . '/cache/');.htaccess
DirectoryIndex index.php # remove the next 3 lines if you see a 500 server error php_flag register_globals off php_flag magic_quotes_gpc off php_value display_errors 0 FileETag none ServerSignature Off Options All -Indexes <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^shorten/(.*)$ shorten.php?longurl=$1 [L] RewriteRule ^([0-9a-zA-Z]{1,6})$ redirect.php?url=$1 [L] </IfModule>License and support
This script is provided as is, without warranty or guarantee, licensed under this creative commons license.
I hope you enjoy!
107 comments skip to comment form
HB said— 27 minutes later
Sean McArthur said— 28 minutes later
Brian Cray said— 33 minutes later
Custom Themes said— 11 hours later
Nicolas said— 11 hours later
Brian Cray said— 12 hours later
robo47 said— 12 hours later
Brian Cray said— 12 hours later
reboltutorial said— 13 hours later
Nicolas said— 15 hours later
Jeremy said— 1 day later
Derek Gathright said— 1 day later
Brian Cray said— 2 days later
Adrien Gibrat said— 2 days later
Adrien Gibrat said— 2 days later
Brian Cray said— 2 days later
Derek Gathright said— 2 days later
Brian Cray said— 2 days later
Derek Gathright said— 2 days later
Brian Cray said— 2 days later
Derek Gathright said— 2 days later
Sean O said— 2 days later
Ochronus said— 3 days later
Ochronus said— 3 days later
Adrien Gibrat said— 3 days later
Brian Cray said— 3 days later
Ochronus said— 3 days later
Matt said— 3 days later
Adrien Gibrat said— 3 days later
Brian Cray said— 3 days later
Brian Cray said— 3 days later
Adrien Gibrat said— 3 days later
Jep said— 4 days later
Brian Cray said— 4 days later
Jep said— 5 days later
Brian Cray said— 6 days later
Ochronus said— 6 days later
Brian Cray said— 6 days later
Jim keller said— 1 week later
Michael said— 2 weeks later
Brian Cray said— 2 weeks later
Michael said— 2 weeks later
Brian Cray said— 2 weeks later
Michael said— 2 weeks later
Michael said— 2 weeks later
Yuan said— 2 weeks later
Nick Yeoman said— 3 weeks later
Adam Arnold said— 1 month later
Amin said— 1 month later
Scripte said— 1 month later
bootcat said— 1 month later
directory said— 1 month later
twe4ked said— 1 month later
ThinkSoJoE said— 1 month later
ThinkSoJoE said— 1 month later
ThinkSoJoE said— 1 month later
Pot said— 2 months later
David D Ochoa said— 2 months later
David D Ochoa said— 2 months later
sp said— 3 months later
Alexsander Akers said— 3 months later
dennyhalim.com said— 3 months later
Brian Cray said— 3 months later
Kit said— 3 months later
Janan said— 3 months later
Mickey said— 4 months later
Brian Cray said— 4 months later
Mickey said— 4 months later
Brian Cray said— 4 months later
Mickey said— 4 months later
Brian Cray said— 4 months later
Icy said— 4 months later
Dylan said— 4 months later
Sanjay said— 4 months later
zizinya said— 5 months later
growco said— 5 months later
Greg Tsioros said— 5 months later
Alvin said— 5 months later
JD said— 5 months later
Less talk, More Example said— 6 months later
mark said— 6 months later
vangel said— 6 months later
Ritz said— 6 months later
Ritz said— 6 months later
Ritz said— 6 months later
vangel said— 6 months later
Kaushik said— 6 months later
Steve said— 6 months later
Brian Cray said— 6 months later
charles said— 6 months later
Brian Cray said— 6 months later
charles said— 6 months later
DmitrySh said— 6 months later
Perry said— 7 months later
Eli J said— 7 months later
Rudolf said— 7 months later
Shane said— 7 months later
Peter Armenti said— 7 months later
Dave said— 8 months later
Pete said— 8 months later
Steve said— 9 months later
Paul said— 9 months later
Bryan said— 9 months later
Gabriel said— 9 months later
Eddie said— 9 months later
Mo said— 10 months later
Gaurav Dadhania said— 10 months later
Respond to this post—