How To Build A PHP Link Shortener With Custom Slugs

A link shortener turns a long URL into a compact address that is easier to share, remember and track. For a small business, creator, affiliate marketer or VTU-style platform, adding custom slugs gives every link a recognisable identity, such as /summer-sale instead of an automatically generated string.

This tutorial shows how to build a simple link shortener with custom slug support in PHP and MySQL. The application will accept a destination URL, allow an optional branded slug, store the record safely, and redirect visitors through a short route such as https://example.com/go/offer.

Plan The URL Shortener

The basic workflow is straightforward: a user submits a long URL and an optional slug, PHP validates the values, the database stores them, and a redirect script looks up the slug before sending the visitor to the original address. You can later add click counts, expiry dates, user accounts and QR codes.

For an Australian audience, short links can be useful in Instagram bios, SMS campaigns, printed flyers in Melbourne, or promotional emails sent to customers in Sydney and Brisbane. The following design keeps the first version small while leaving room for future features.

Feature Simple Version Possible Upgrade
Destination URL One validated URL Domain allowlists and malware scanning
Short code User-selected custom slug Automatic fallback code
Storage MySQL with PDO Multiple users and workspaces
Redirect HTTP 302 response Analytics and device targeting
Administration Basic creation form Login-protected dashboard

A short link should communicate its purpose without exposing unnecessary information. Slugs such as eofy-sale, sms-offer and course-login are easier to use in campaigns than random values. “EOFY” is especially familiar in the Australian business calendar, although your marketing claims must still comply with the Australian Consumer Law.

Create The Database

Create a database and a table for each shortened link. The slug column must be unique, because two records cannot share the same path. Using a numeric ID as the primary key makes each record efficient to reference, while timestamps help with maintenance and reporting.

CREATE TABLE short_links (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    slug VARCHAR(80) NOT NULL UNIQUE,
    target_url TEXT NOT NULL,
    clicks INT UNSIGNED NOT NULL DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Use PDO with prepared statements instead of inserting form values directly into SQL. Prepared statements prevent SQL injection and make your code easier to extend. Store the complete destination URL, including the scheme, so that redirects behave consistently.

<?php
$pdo = new PDO(
    'mysql:host=localhost;dbname=shortener;charset=utf8mb4',
    'db_user',
    'db_password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
);

Keep database credentials outside a publicly accessible directory whenever your hosting environment allows it. On shared hosting, use environment variables or a configuration file protected by server rules rather than placing credentials in a downloadable web folder.

Build The Redirect Endpoint

Assume your short URLs follow the format /go/slug. The redirect file can read the slug from the query string or from a rewrite rule. A query-string version is easier to test first: redirect.php?slug=eofy-sale.

<?php
require __DIR__ . '/database.php';

$slug = $_GET['slug'] ?? '';

$stmt = $pdo->prepare(
    'SELECT id, target_url FROM short_links WHERE slug = :slug LIMIT 1'
);
$stmt->execute(['slug' => $slug]);
$link = $stmt->fetch();

if (!$link) {
    http_response_code(404);
    exit('Short link not found.');
}

$pdo->prepare(
    'UPDATE short_links SET clicks = clicks + 1 WHERE id = :id'
)->execute(['id' => $link['id']]);

header('Location: ' . $link['target_url'], true, 302);
exit;

A temporary 302 redirect is useful while testing because you may change the destination later. A permanent 301 response can be cached by browsers and search engines, making corrections harder. The click counter in this example records total visits, but it does not store IP addresses or detailed personal information.

For a cleaner address, add this Apache rule to .htaccess:

RewriteEngine On
RewriteRule ^go/([A-Za-z0-9_-]+)$ redirect.php?slug=$1 [L,QSA]

The expression permits letters, numbers, underscores and hyphens. It rejects spaces and punctuation that could create confusing or unsafe paths.

Validate Custom Slugs

Custom slug validation is the main feature of this project. A slug should be short, readable and predictable. Convert it to lowercase, trim unnecessary whitespace and allow only a controlled character set.

$rawSlug = trim($_POST['slug'] ?? '');

$slug = strtolower($rawSlug);
$slug = preg_replace('/\s+/', '-', $slug);

if (!preg_match('/^[a-z0-9-]{3,80}$/', $slug)) {
    exit('Use 3 to 80 lowercase letters, numbers or hyphens.');
}

Reserve system words such as admin, login, api, go and dashboard. Without a reserved-word check, somebody could create a slug that conflicts with an existing application route. You should also reject repeated hyphens if you want a cleaner naming standard.

$reserved = ['admin', 'login', 'api', 'go', 'dashboard'];

if (in_array($slug, $reserved, true)) {
    exit('That slug is reserved.');
}

When a slug already exists, show a useful error rather than silently replacing the old destination. If links are used in paid campaigns, replacing a slug unexpectedly can send existing visitors to the wrong offer and create customer service problems.

Save Links With A Form

The creation form needs a destination URL and an optional custom slug. If the slug is blank, generate a random fallback so users do not have to invent a name every time. For a production application, add CSRF protection and authentication before allowing public submissions.

<?php
require __DIR__ . '/database.php';

$message = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $target = trim($_POST['target_url'] ?? '');
    $slug = strtolower(trim($_POST['slug'] ?? ''));

    if (!filter_var($target, FILTER_VALIDATE_URL)) {
        exit('Enter a valid URL.');
    }

    if ($slug === '') {
        $slug = bin2hex(random_bytes(4));
    }

    if (!preg_match('/^[a-z0-9-]{3,80}$/', $slug)) {
        exit('Invalid custom slug.');
    }

    $stmt = $pdo->prepare(
        'INSERT INTO short_links (slug, target_url)
         VALUES (:slug, :target_url)'
    );

    try {
        $stmt->execute([
            'slug' => $slug,
            'target_url' => $target
        ]);

        $message = 'Short link created: /go/' . $slug;
    } catch (PDOException $e) {
        $message = 'That slug is already in use.';
    }
}

A matching HTML form can remain simple:

<form method="post">
  <label for="target_url">Long URL</label>
  <input id="target_url" name="target_url" type="url" required>

  <label for="slug">Custom slug</label>
  <input id="slug" name="slug" pattern="[a-zA-Z0-9-]{3,80}">

  <button type="submit">Create Short Link</button>
</form>

Escape any database value before displaying it in HTML with htmlspecialchars(). If your shortener supports public use, apply rate limits and consider requiring an account. Otherwise, automated users may flood the database with links or abuse your domain for spam.

Protect Privacy And Improve Performance

A redirect service handles URLs that may contain tracking parameters, customer identifiers or private campaign data. The Australian Privacy Act 1988 and the Australian Privacy Principles are relevant when your application collects personal information, such as account details, IP addresses or detailed visitor analytics. Avoid collecting data that you do not genuinely need.

If you build email campaigns for the link shortener, review your obligations under the Spam Act 2003. Businesses should obtain appropriate consent, identify the sender and provide an unsubscribe option. A useful resource on growing a permission-based audience is this lead magnet guide.

Add indexes to fields used in lookups, especially the unique slug column. Keep redirects fast by selecting only the columns required for the response. If traffic grows, cache popular slugs carefully, but invalidate the cache whenever a destination changes.

You can also restrict destination URLs to https and block unsafe schemes such as javascript:. For a business used across Perth, Adelaide or regional areas, reliable Australian hosting and a content delivery network can reduce latency, although the database and security controls remain more important than raw speed.

Launch And Grow The Tool

Before publishing the shortener, test valid URLs, invalid URLs, duplicate slugs, reserved names, blank fields and very long input. Confirm that the 404 response works and that the redirect does not expose database errors. Test the form on mobile devices because many customers will create or share links from a phone.

Launch Checklist

Once the core version works, add an authenticated dashboard, link expiry, QR-code generation, campaign labels and exportable click reports. A creator promoting a digital product can use one slug for YouTube, another for a newsletter and another for printed material, then compare performance without manually inspecting every destination URL.

Start with the PHP and MySQL version above, place it on a test subdomain, and create a few branded links for real campaigns. After validating the workflow, connect it to your website or marketing system and gradually add analytics, user accounts and stronger moderation controls.