How I Built My Own URL Shortener (No Third Party API Required)Every time I needed to share a long link in a message or on a form, I found myself pasting it into some external service and hoping it would still work months later. Eventually I decided to just build my own shortener. It took less than an hour, runs entirely on infrastructure I already control, and has no usage limits since I'm not depending on anyone else's API.
Here's exactly how I did it.
Why Build My Own Instead of Using a Free API
Services like Bitly or TinyURL are convenient, but they come with tradeoffs. Free tiers eventually cap how many links you can create. Your data lives on someone else's servers. And if the service shuts down or changes its terms, every link you've ever shared could break.
Since I already work with PHP and MySQL for most of my projects, building a shortener myself meant I could keep everything under my own domain, skip rate limits entirely, and add features like click tracking whenever I wanted.
Step 1: Setting Up the Database
The whole system really only needs one table. It stores the short code, the original URL, and a click counter for basic analytics.
CREATE TABLE urls (
id INT AUTO_INCREMENT PRIMARY KEY,
short_code VARCHAR(10) UNIQUE NOT NULL,
original_url TEXT NOT NULL,
clicks INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Nothing fancy here. A unique index on short_code is the important part since that's what gets looked up on every redirect.
Step 2: Writing the Shorten Endpoint
This is the script that takes a long URL and generates a short code for it. I used uniqid() converted into a compact base 36 string so codes stay short while remaining unique enough for practical use.
<?php
$conn = new mysqli($host, $user, $pass, $db);
$url = $_POST['url'];
$code = substr(base_convert(uniqid(), 16, 36), 0, 6);
$stmt = $conn->prepare("INSERT INTO urls (short_code, original_url) VALUES (?, ?)");
$stmt->bind_param("ss", $code, $url);
$stmt->execute();
echo json_encode(['short_url' => "https://yourdomain.com/r/$code"]);This gives me a simple JSON API. Send a POST request with a url field, get back a short link.
Step 3: Handling Redirects
Once a short code exists, visiting it needs to look up the original URL and forward the visitor there. I also increment the click counter at the same time so I have basic usage stats without needing an external analytics tool.
<?php
$code = $_GET['code'];
$stmt = $conn->prepare("SELECT original_url FROM urls WHERE short_code = ?");
$stmt->bind_param("s", $code);
$stmt->execute();
$result = $stmt->get_result()->fetch_assoc();
if ($result) {
$conn->query("UPDATE urls SET clicks = clicks + 1 WHERE short_code = '$code'");
header("Location: " . $result['original_url']);
exit;
} else {
http_response_code(404);
echo "Not found";
}Step 4: Making the URLs Clean
By default the redirect script would need to be accessed like r.php?code=abc123, which isn't exactly what you want a short link to look like. A quick rewrite rule fixes that.
RewriteEngine On
RewriteRule ^r/([a-zA-Z0-9]+)$ r.php?code=$1 [L,QSA]With this in place, yourdomain.com/r/abc123 works exactly like a real short link should.
What I'd Add Next
The base version works, but there are a few things worth building on top of it if you go this route yourself:
Custom aliases so users can choose their own short code instead of a random one. Expiry dates for links that shouldn't last forever. Basic authentication so not just anyone can generate links through the API. A small dashboard to see which links are getting the most clicks.
Final Thoughts
Building a URL shortener turned out to be a great small project for understanding how these services work under the hood. It's genuinely unlimited since there's no external quota to run into, and because I control the database, I can extend it however I need. If you already work with a backend language and a database, this is a weekend project, not a big undertaking.
Hit me with a comment!