Generate Unique URL Slug in Codeigniter
If you have ever visited a well-structured blog or e-commerce site and
noticed clean, readable URLs like
/post/how-to-build-a-rest-api-in-php instead of something
like /post?id=47 โ that readable version is called a
URL slug. It is better for users, better for SEO, and
it is something every serious web application should implement. In this
guide we will walk through exactly how to generate unique, SEO-friendly
URL slugs dynamically in CodeIgniter using PHP.
What Is a URL Slug and Why Does It Matter?
A URL slug is the human-readable part of a URL that identifies a specific page. It is generated from the title or name of the content โ converting spaces to hyphens, stripping special characters, and making everything lowercase. Here is the difference in practice:
-
Generic URL:
www.tipsandtricks.dev/post/1 -
SEO-friendly URL:
www.tipsandtricks.dev/post/how-to-generate-unique-url-slug-in-codeigniter
The second version tells both the user and search engines exactly what the page is about before they even click it. Google uses the words in your URL as a ranking signal โ so a descriptive slug directly contributes to your SEO. It also improves click-through rates because people are more likely to click a URL they can read and trust.
๐ก Want to generate SEO-friendly slugs instantly? Try our free AI-powered Slug Generator tool โ it generates 5 optimized slug suggestions from your title in one click: tipsandtricks.dev/tools/slug-generator โ
What We Will Build
By the end of this tutorial you will have a working CodeIgniter function that:
- Converts any post title into a clean, lowercase, hyphenated slug
- Checks the database to ensure the slug is unique
- Automatically appends a number (
-1,-2) if the slug already exists - Stores the slug in the database alongside the post
- Works for blogs, e-commerce products, portfolios โ any content type
Step 1 โ Create the Database Table
Your posts table needs at minimum three columns โ an auto-increment ID, the post title, and the URL slug. Run this SQL in your database:
CREATE TABLE `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`url_slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL UNIQUE
);
The UNIQUE constraint on url_slug is important
โ it enforces uniqueness at the database level as a safety net, even if
our PHP logic handles it first. Never rely on application code alone
for uniqueness constraints.
Step 2 โ Create the Model
Add this to application/models/Post_model.php. It handles
inserting data and checking for existing slugs:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Post_model extends CI_Model {
public function __construct() {
parent::__construct();
$this->load->database();
}
// Insert a new post into the database
public function insert_post($data) {
return $this->db->insert('posts', $data);
}
// Check if a slug already exists in the database
public function slug_exists($slug, $exclude_id = NULL) {
$this->db->where('url_slug', $slug);
if ($exclude_id) {
$this->db->where('id !=', $exclude_id);
}
return $this->db->get('posts')->num_rows() > 0;
}
// Get all posts (for display)
public function get_all_posts() {
return $this->db->get('posts')->result();
}
// Get post by slug
public function get_post_by_slug($slug) {
return $this->db->where('url_slug', $slug)
->get('posts')
->row();
}
}
Step 3 โ The Slug Generator Function
This is the core function. Add it to your controller or a helper file. It converts a title to a slug and keeps appending a number until it finds a slug that does not already exist in the database:
/**
* Generate a unique SEO-friendly URL slug
*
* @param string $string The title or text to slugify
* @param string $table The database table to check uniqueness against
* @param string $field The column name that stores slugs
* @param string|null $key Optional: column name to exclude a record (for updates)
* @param mixed|null $value Optional: value of the exclusion column (for updates)
* @return string The unique slug
*/
function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
$ci =& get_instance();
$ci->load->helper('url'); // ensure url helper is loaded
// Step 1 โ convert title to slug format
$slug = url_title($string); // converts spaces to hyphens
$slug = strtolower($slug); // make everything lowercase
$slug = preg_replace('/[^a-z0-9\-]/', '', $slug); // remove special chars
// Step 2 โ build the initial WHERE params
$i = 0;
$params = [];
$params[$field] = $slug;
// Exclude current record if updating (prevents false conflicts with itself)
if ($key) {
$params["$key !="] = $value;
}
// Step 3 โ keep checking until slug is unique
while ($ci->db->where($params)->get($table)->num_rows()) {
if (!preg_match('/-{1}[0-9]+$/', $slug)) {
// First conflict โ append -1
$slug .= '-' . ++$i;
} else {
// Subsequent conflicts โ increment the existing number
$slug = preg_replace('/[0-9]+$/', ++$i, $slug);
}
$params[$field] = $slug;
}
return $slug;
}
How It Works โ Step by Step
-
Title "How to Reverse a String" โ slug:
how-to-reverse-a-string -
If
how-to-reverse-a-stringalready exists in DB โ becomeshow-to-reverse-a-string-1 -
If
how-to-reverse-a-string-1also exists โ becomeshow-to-reverse-a-string-2 - And so on until a unique slug is found
Step 4 โ The Controller
Add this to application/controllers/Welcome.php:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Welcome extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->helper(['form', 'url']);
$this->load->model('Post_model');
}
// Show the form
public function index() {
$data['posts'] = $this->Post_model->get_all_posts();
$this->load->view('index', $data);
}
// Handle form submission โ generate slug and save to DB
public function add() {
$title = $this->input->post('title');
// Validate โ title is required
if (empty(trim($title))) {
redirect('index');
}
// Generate unique SEO slug from the title
$slug = generate_url_slug(
$title,
'posts',
'url_slug'
);
// Save to database
$data = [
'title' => $title,
'url_slug' => $slug,
];
$this->Post_model->insert_post($data);
redirect('index');
}
// Display a post by its slug
public function view($slug) {
$post = $this->Post_model->get_post_by_slug($slug);
if (!$post) {
show_404();
}
$data['post'] = $post;
$this->load->view('post', $data);
}
// Slug generator function (can also be moved to a helper)
function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
$ci =& get_instance();
$slug = strtolower(url_title($string));
$slug = preg_replace('/[^a-z0-9\-]/', '', $slug);
$i = 0;
$params = [$field => $slug];
if ($key) $params["$key !="] = $value;
while ($ci->db->where($params)->get($table)->num_rows()) {
if (!preg_match('/-{1}[0-9]+$/', $slug))
$slug .= '-' . ++$i;
else
$slug = preg_replace('/[0-9]+$/', ++$i, $slug);
$params[$field] = $slug;
}
return $slug;
}
}
Step 5 โ The View (index.php)
Add this to application/views/index.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Generate URL Slug โ CodeIgniter</title>
</head>
<body>
<h2>Create a New Post</h2>
<form method="post" action="<?= base_url('index.php/Welcome/add') ?>">
<label for="title">Post Title:</label><br>
<input type="text" id="title" name="title"
placeholder="Enter your post title..." style="width:400px; padding:8px;">
<button type="submit" style="padding:8px 16px;">Generate Slug & Save</button>
</form>
<hr>
<h3>All Posts</h3>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<strong><?= htmlspecialchars($post->title) ?></strong>
โ Slug: <code><?= $post->url_slug ?></code>
โ <a href="<?= base_url('index.php/Welcome/view/' . $post->url_slug) ?>">View Post</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>
Step 6 โ Configure Routes (Optional but Recommended)
To get clean URLs without index.php in the path, add these
routes to application/config/routes.php:
// Map /post/your-slug-here to the view method
$route['post/(:any)'] = 'welcome/view/$1';
// Home page
$route['default_controller'] = 'welcome';
$route['404_override'] = '';
Also make sure your .htaccess file in the project root removes
index.php from URLs:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
Real World Example โ Slug Generation Output
// Input titles and generated slugs
"Hello World" โ hello-world
"How to Reverse a String" โ how-to-reverse-a-string
"How to Reverse a String" โ how-to-reverse-a-string-1 (duplicate)
"How to Reverse a String" โ how-to-reverse-a-string-2 (third entry)
"CSS Tips & Tricks!" โ css-tips-tricks
"Laravel 12 โ What's New?" โ laravel-12-whats-new
"100% Free PHP Tutorial" โ 100-free-php-tutorial
Tip โ Move the Function to a Helper for Reuse
If you need this function across multiple controllers, move it to a custom helper file so you only write it once:
// application/helpers/slug_helper.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
if (!function_exists('generate_url_slug')) {
function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
$ci =& get_instance();
$slug = strtolower(url_title($string));
$slug = preg_replace('/[^a-z0-9\-]/', '', $slug);
$i = 0;
$params = [$field => $slug];
if ($key) $params["$key !="] = $value;
while ($ci->db->where($params)->get($table)->num_rows()) {
if (!preg_match('/-{1}[0-9]+$/', $slug))
$slug .= '-' . ++$i;
else
$slug = preg_replace('/[0-9]+$/', ++$i, $slug);
$params[$field] = $slug;
}
return $slug;
}
}
Load it in your controller or autoload it in config/autoload.php:
// In your controller
$this->load->helper('slug');
// Or in application/config/autoload.php โ loads automatically everywhere
$autoload['helper'] = ['url', 'form', 'slug'];
๐ ๏ธ Try Our Free AI Slug Generator Tool
Not sure what slug to use for your post? Our free AI-powered Slug Generator tool gives you 5 SEO-optimized slug suggestions from any title โ instantly. No signup, no cost, just paste your title and get ready-to-use slugs:
๐ค AI Slug Generator โ Free Tool
Enter any post title and get 5 AI-generated SEO-friendly slug suggestions in one click. Perfect for blogs, portfolios, and e-commerce products.
Try the Slug Generator โFree ยท No signup ยท 5 AI suggestions per title
Common Mistakes to Avoid
1. Not sanitizing the input string
// โ Wrong โ special characters can break URLs
$slug = str_replace(' ', '-', $title); // misses ?, &, #, % etc.
// โ
Correct โ use url_title() + regex to strip all non-slug characters
$slug = strtolower(url_title($string));
$slug = preg_replace('/[^a-z0-9\-]/', '', $slug);
2. No uniqueness check
// โ Wrong โ if two posts have the same title, second one overwrites the first
$slug = strtolower(url_title($title));
$this->db->insert('posts', ['title' => $title, 'url_slug' => $slug]);
// โ
Correct โ always run the uniqueness check function before inserting
$slug = generate_url_slug($title, 'posts', 'url_slug');
$this->db->insert('posts', ['title' => $title, 'url_slug' => $slug]);
3. Not handling updates (editing existing posts)
// โ Wrong โ generates a new conflicting slug when updating the same post
$slug = generate_url_slug($title, 'posts', 'url_slug');
// โ
Correct โ exclude the current post ID to avoid false conflicts
$slug = generate_url_slug($title, 'posts', 'url_slug', 'id', $post_id);
Final Thought
SEO-friendly URL slugs are a small implementation detail that makes a real difference โ in search engine rankings, in click-through rates, and in how professional your application looks and feels. The function we built here handles all the edge cases: special characters, duplicates, numbered suffixes, and update conflicts.
Once you add this to your CodeIgniter project you will never need to think about URL slugs again โ every post, product, or page gets a clean, unique, SEO-optimized URL automatically.
And if you want to experiment with slug ideas before writing any code, try our free AI Slug Generator tool โ it gives you 5 optimized slug suggestions from any title instantly.
Have questions or running into a specific issue with your CodeIgniter setup? Drop us a message at support@tipsandtricks.dev or use our contact page.
๐ You Might Also Like
- โ Best Google AdSense Alternative 2026 - Monetag Review for Publishers miscellaneous
- โ How to Start Freelancing as a Web Developer in 2026 (Complete Beginner's Guide) miscellaneous
- โ HTTP QUERY Method (RFC 10008) โ The New HTTP Method Every Developer Should Know miscellaneous
- โ JavaScript Array Methods Cheat Sheet โ Complete Guide with Example javascript
- โ MyLync โ Best Free Linktree Alternative for Developers, Creators & Businesses miscellaneous