मैंने अपना Custom PHP Blog Admin Panel AI की मदद से कैसे बनाया — पूरी कहानी Step by Step

सच बताऊं तो जब मैंने पहली बार सोचा कि अपना खुद का blog admin panel बनाऊंगा, तो लगा था यह काम बस कुछ घंटों का है। लेकिन जैसे-जैसे काम आगे बढ़ा, समझ आया कि एक proper, secure, और user-friendly admin panel बनाना — जिसमें WYSIWYG editor हो, image upload हो, Hindi support हो, और security भी tight हो — यह इतना आसान नहीं था।

इस पूरी journey में मैंने Claude AI की मदद ली और जो result आया, वो सच में काबिल-ए-तारीफ था। आज इस article में मैं आपको step by step बताऊंगा कि हमने क्या किया, कौन-कौन सी गलतियां हुईं, उन्हें कैसे fix किया, और आप भी अपना custom blog system कैसे बना सकते हैं।

अगर आप WordPress से थक गए हैं, या फिर lightweight और fast blog system चाहते हैं जो पूरी तरह आपके control में हो — तो यह article आपके लिए ही है।

पहले समझते हैं — WordPress क्यों नहीं?

देखिए, WordPress बुरा नहीं है। लेकिन जब आप एक simple coding tutorial blog चला रहे हों, तो WordPress का पूरा infrastructure काफी heavy लगता है। हर plugin एक नई dependency है, हर update एक नया risk है, और hosting पर load भी ज्यादा पड़ता है।

मेरी जरूरतें बिल्कुल clear थीं:

  1. Fast loading blog जो SEO-friendly हो
  2. Hindi और English दोनों में content लिखने की सुविधा
  3. Simple admin panel जो मैं खुद manage कर सकूं
  4. Direct image upload — कोई third-party CDN नहीं
  5. Tight security — SQL injection, XSS, CSRF सब से बचाव

इसीलिए मैंने decide किया कि PHP + MySQL से scratch में बनाऊंगा। और Claude AI को अपना coding partner बनाया।

Project की शुरुआत — Database Structure

हर चीज की नींव database होता है। हमने पहले यह table बनाई:

CREATE TABLE blog_posts (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(500) NOT NULL,
slug VARCHAR(300) UNIQUE NOT NULL,
excerpt TEXT,
content LONGTEXT,
cover_image VARCHAR(500),
category VARCHAR(100),
tags TEXT,
meta_title VARCHAR(300),
meta_description TEXT,
status ENUM('draft', 'published') DEFAULT 'draft',
views INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

यहां एक important बात — utf8mb4 charset use करना जरूरी है। यह Hindi, Emoji और सभी Unicode characters को properly store करता है। अगर आप utf8 use करते हैं तो Hindi characters corrupt हो सकते हैं।

Config File — Foundation जो सब चीजों को जोड़ती है

हमने एक clean config.php बनाई जिसमें सभी constants defined हैं:

define('DB_HOST', 'localhost');
define('DB_USER', 'your_username');
define('DB_PASS', 'your_password');
define('DB_NAME', 'your_database');
define('SITE_URL', 'https://yourdomain.com');
define('UPLOAD_DIR', __DIR__ . '/uploads/blog/');
define('UPLOAD_URL', 'https://yourdomain.com/uploads/blog/');
define('ADMIN_USER', 'your_admin_name');
define('ADMIN_PASS_HASH', password_hash('your_password', PASSWORD_BCRYPT));

एक बहुत important बात — password plain text में कभी मत रखो। हमने password_hash() function use किया जो BCrypt algorithm से एक secure hash बनाता है। यह hash कभी reverse नहीं किया जा सकता।

Upload URL को हमेशा full absolute URL रखो। यह गलती हमने खुद की थी — जब हमने relative path /uploads/blog/ रखी, तो images blog post pages पर load नहीं हो रही थीं क्योंकि browser wrong path construct कर रहा था।

Admin Panel — Security पहले, Design बाद में

Admin panel बनाते वक्त Claude ने मुझे पहले security पर focus करने को कहा। यह बिल्कुल सही approach था। एक insecure admin panel basically आपके पूरे server का दरवाजा खोल देता है।

CSRF Protection

CSRF यानी Cross-Site Request Forgery। इसमें कोई attacker आपसे अनजाने में ऐसा form submit करवा सकता है जो आप नहीं चाहते। इसे रोकने के लिए हमने हर form में एक secret token डाला:

function generate_csrf_token() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}

function verify_csrf_token($token) {
if (!hash_equals($_SESSION['csrf_token'], $token)) {
die('Security error!');
}
}

hash_equals() use करना जरूरी है — यह timing attack से बचाता है। अगर आप normal === use करें तो attacker timing analysis से token guess कर सकता है।

SQL Injection से बचाव — Prepared Statements

यह सबसे common और dangerous vulnerability है। Original code में raw queries थीं:

// खतरनाक — कभी मत करो!
$conn->query("SELECT * FROM blog_posts WHERE slug='$slug'");

अगर कोई slug में ' OR '1'='1 डाल दे, तो सारा database expose हो जाता है। हमने यह fix किया:

// सही तरीका — Prepared Statement
$stmt = $conn->prepare("SELECT * FROM blog_posts WHERE slug = ? AND status = 'published'");
$stmt->bind_param("s", $slug);
$stmt->execute();

Prepared statements में user input और query बिल्कुल अलग रहते हैं। Database engine पहले query compile करता है, फिर parameter add करता है — injection possible ही नहीं होता।

Session Security

Session hijacking रोकने के लिए:

ini_set('session.cookie_httponly', 1); // JavaScript से cookie access नहीं
ini_set('session.cookie_secure', 1); // HTTPS only
ini_set('session.cookie_samesite', 'Strict'); // Cross-site requests block
session_regenerate_id(true); // Login पर नया session ID

Brute Force Protection

हमने login attempts limit भी लगाई — 5 wrong attempts के बाद 5 मिनट का lockout:

if (!isset($_SESSION['login_attempts'])) $_SESSION['login_attempts'] = 0;

if ($_SESSION['login_attempts'] >= 5 && (time() - $_SESSION['last_attempt']) < 300) {
$login_error = 'Too many attempts. Wait 5 minutes.';
} else {
if (password_verify($_POST['p'], ADMIN_PASS_HASH)) {
session_regenerate_id(true);
$_SESSION['blog_admin'] = true;
$_SESSION['login_attempts'] = 0;
} else {
$_SESSION['login_attempts']++;
$_SESSION['last_attempt'] = time();
}
}

WYSIWYG Editor — Hindi और English दोनों के लिए

यह project का सबसे interesting हिस्सा था। हमें एक ऐसा editor चाहिए था जो:

  1. WordPress जैसा visual editing दे
  2. Hindi (Devanagari) fonts support करे
  3. HTML mode भी हो power users के लिए
  4. H1, H2, H3, Bold, Italic, Lists सब formatting options हों

हमने Quill.js library choose की। यह open source है, lightweight है, और highly customizable है।

var quill = new Quill('#quill-editor', {
theme: 'snow',
modules: {
toolbar: [
[{ 'header': [1, 2, 3, 4, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
['blockquote', 'code-block'],
['link', 'image'],
['clean']
]
}
});

Hindi Font Support

हिंदी लिखने के लिए हमने Google Fonts से Noto Sans Devanagari load की। यह font Hindi characters को beautifully render करती है और web safe भी है।

हमने एक font picker भी बनाया — English, हिंदी (Noto), Mangal, और Kokila fonts के buttons। User जिस font में लिखना चाहे, एक click में switch कर सकता है।

Visual ↔ HTML Toggle

Experienced bloggers sometimes HTML directly likhna chahte hain। हमने दो tabs बनाए — Visual Editor और HTML Source. दोनों के बीच switch करते वक्त content automatically sync होता है:

function switchTab(tab) {
if (tab === 'visual') {
// HTML → Quill
quill.root.innerHTML = document.getElementById('html-editor').value;
} else {
// Quill → HTML
document.getElementById('html-editor').value = quill.root.innerHTML;
}
}

// Form submit पर content sync
document.getElementById('post-form').addEventListener('submit', function() {
document.getElementById('content-field').value =
currentTab === 'visual' ? quill.root.innerHTML : document.getElementById('html-editor').value;
});

Image Upload — Server पर Direct

यह feature बहुत important था। पहले सिर्फ URL paste करने का option था। हमने proper file upload implement किया।

Server Side Validation

File upload के साथ सबसे बड़ा risk है — कोई PHP file upload करके server पर run करे। इसे रोकने के लिए multiple layers of validation:

// 1. MIME type check — extension नहीं, actual file content check
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

if (!in_array($mime, $allowed)) {
return ['error' => 'Only images allowed!'];
}

// 2. File size check — max 5MB
if ($file['size'] > 5 * 1024 * 1024) {
return ['error' => 'Max 5MB allowed'];
}

// 3. Actually verify it's an image
if (@getimagesize($file['tmp_name']) === false) {
return ['error' => 'Invalid image file'];
}

// 4. Random filename — original name से कोई issue नहीं
$filename = 'blog_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;

Upload Folder Security

Upload folder में automatically एक .htaccess create होती है जो PHP execution block करती है:

php_flag engine off
Options -ExecCGI

यह बहुत important है। अगर कोई किसी तरह PHP file upload कर दे, तो भी execute नहीं होगी।

.htaccess — Clean URLs का जादू

हमें URLs को /blog/post-slug/ format में चाहिए था, लेकिन actually file post.php?slug=post-slug पर है। यह magic .htaccess करती है:

Options -Indexes
RewriteEngine On
RewriteBase /blog/

# Real files/folders को bypass करो
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# Clean URL → post.php?slug=...
RewriteRule ^([a-zA-Z0-9][a-zA-Z0-9\-]*)/?$ post.php?slug=$1 [L,QSA,NC]

एक important lesson — RewriteBase /blog/ line बहुत जरूरी है अगर आपका blog एक subfolder में है। यह line नहीं होने से हमें 404 errors आ रहे थे।

और हां — negative lookahead (?!) syntax कुछ hosting providers (जैसे Hostinger) पर properly काम नहीं करता। इसलिए हमने simple approach use की — real files automatically pass होती हैं RewriteCond -f से।

Post.php — Blog Post Display Page

यह वो page है जो visitors देखते हैं। इसमें कई important features हैं।

Light/Dark Mode Toggle

Readability के लिए हमने दोनों modes implement किए। User का choice localStorage में save होता है:

function setTheme(t) {
document.documentElement.setAttribute('data-theme', t);
localStorage.setItem('cc_theme', t);
themeBtn.textContent = t === 'dark' ? '☀️' : '🌙';
}

// Page load पर saved preference apply करो
const saved = localStorage.getItem('cc_theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
setTheme(saved);

Auto Table of Contents

Post में सभी H2 और H3 headings को automatically detect करके sidebar में TOC बनती है:

const headings = content.querySelectorAll('h2, h3');
headings.forEach((h, i) => {
if (!h.id) h.id = 'h-' + i;
// TOC link बनाओ
const a = document.createElement('a');
a.href = '#' + h.id;
a.textContent = h.textContent;
});

Reading Progress Bar

Page के top पर एक progress bar है जो scroll के साथ fill होती है — यह user को बताती है कि article कितना पढ़ा गया।

View Counter

Session-based view counting — same user same post को बार-बार view न करे:

$session_key = 'viewed_' . (int)$post['id'];
if (empty($_SESSION[$session_key])) {
$stmt = $conn->prepare("UPDATE blog_posts SET views = views + 1 WHERE id = ?");
$stmt->bind_param("i", $post['id']);
$stmt->execute();
$_SESSION[$session_key] = true;
}

वो Bugs जो हमें परेशान करते रहे

सच कहूं तो यह project बिना bugs के पूरा नहीं हुआ। कुछ ऐसी गलतियां हुईं जो बहुत सिखाने वाली थीं।

Bug 1 — $post Variable का गायब होना

यह सबसे frustrating bug था। Post page खुल रही थी लेकिन title, content, date — सब blank था। Date 01 Jan 1970 दिख रही थी (यह Unix timestamp 0 का result है)।

कारण था — code में $post_row = $result->fetch_assoc() था, लेकिन आगे $post use हो रहा था। Variable assignment ही नहीं हुई थी! PHP ने silently empty array बना दिया।

Fix बहुत simple था:

$post = $result->fetch_assoc(); // सीधे $post में assign करो

Bug 2 — Images Load नहीं हो रही थीं

Database में image path /uploads/blog/image.webp था — relative path। जब user /blog/post-slug/ पर होता था, तो browser इसे /blog/uploads/blog/image.webp समझता था जो exist नहीं करता।

Fix — UPLOAD_URL में full absolute URL:

define('UPLOAD_URL', 'https://yourdomain.com/uploads/blog/');

और जो old posts थीं, उनके लिए database update:

UPDATE blog_posts
SET cover_image = CONCAT('https://yourdomain.com', cover_image)
WHERE cover_image LIKE '/uploads/%';

Bug 3 — Admin Panel 403 Error

जब हमने .htaccess update किया, तो admin.php खुद ही block हो गई! कारण था negative lookahead regex जो Hostinger पर support नहीं था।

Fix — Simple approach:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

Real files automatically allow हो जाती हैं। Complex regex की जरूरत नहीं।

Bug 4 — Session Regenerate हर Page पर

Security के नाम पर हमने session_regenerate_id(true) हर page load पर लगाया था। इससे view counter session key हर बार change हो जाती थी और हर refresh पर view count बढ़ता था।

Fix — Session regenerate सिर्फ login/logout पर करो।

SEO Optimization — जो हमने Build में Include किया

SEO सिर्फ keywords नहीं होता। Technical SEO भी बहुत important है।

Meta Tags

<title>Post Title | CodeCraft</title>
<meta name="description" content="...">
<link rel="canonical" href="https://domain.com/blog/slug/">

Open Graph (Social Sharing)

<meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:image" content="...">
<meta property="og:type" content="article">

JSON-LD Structured Data

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Post Title",
"datePublished": "2026-03-16",
"author": {"@type": "Organization", "name": "CodeCraft"}
}
</script>

यह Google को clearly बताता है कि यह article है, कब publish हुआ, और किसने लिखा। इससे rich snippets मिल सकते हैं search results में।

Slug — URL Structure

Clean, keyword-rich slugs SEO के लिए बहुत important हैं। हमने admin panel में auto-slug generation भी लगाई — title type करते ही slug automatically बनती है:

function toSlug(s) {
return s.toLowerCase().trim()
.replace(/[\u0900-\u097F]/g, '') // Hindi characters remove (URL में)
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-');
}

Design Philosophy — Readability First

Blog का design readability के आसपास बनाना चाहिए था। हमने Astra theme जैसा approach लिया:

  1. Font size — 18px base, content 20px। छोटे font से आंखें थकती हैं
  2. Line height — 1.9-2.0। Tight lines padhne mein dikkat hoti hai
  3. Max width — Content column 760px max। Wide column पढ़ना मुश्किल होता है
  4. Light mode default — Most readers light background पसंद करते हैं
  5. Hindi font — Noto Sans Devanagari जो web पर सबसे readable है

Performance Tips

अगर आप यह system खुद implement करें तो कुछ performance optimizations जरूर करें:

  1. Images के लिए loading="lazy" attribute — above the fold images को छोड़कर
  2. Google Fonts को rel="preconnect" से preload करें
  3. Content को LONGTEXT में store करें — large Hindi content के लिए
  4. Database में slug column पर index लगाएं — queries fast होंगी
  5. Static assets को browser cache करने दें .htaccess से

आगे क्या करें — Future Improvements

यह system अभी भी evolve हो रहा है। कुछ चीजें हैं जो add करनी हैं:

  1. Comment System — Readers की engagement के लिए
  2. Search — Posts search करने की functionality
  3. Categories Page — Category-wise posts listing
  4. RSS Feed — Readers को subscribe करने दें
  5. Sitemap Auto-generation — New posts automatically sitemap में
  6. Media Library — Uploaded images को manage करने की UI

क्या यह सबके लिए सही है?

यह एक valid सवाल है। अगर आप technically comfortable हैं PHP और MySQL के साथ, तो यह approach बहुत powerful है। आपका पूरा control है — कोई plugin dependency नहीं, कोई forced update नहीं, कोई unnecessary features नहीं जो आप use नहीं करते।

लेकिन अगर आप non-technical हैं या चाहते हैं कि सब कुछ out-of-the-box काम करे, तो WordPress या Ghost जैसे platforms better हैं।

मेरे लिए personally यह experience बहुत valuable था। एक blog system scratch से बनाकर मुझे PHP, MySQL security, Apache rewriting, file upload security, और frontend performance के बारे में जितना सीखा — वो किसी tutorial से नहीं सीखा जा सकता था।

Security Headers — वो Extra Layer जो अक्सर भूल जाते हैं

PHP में response headers add करना एक simple लेकिन बहुत effective security measure है। हमने post.php में यह headers लगाए:

header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("X-XSS-Protection: 1; mode=block");
header("Strict-Transport-Security: max-age=31536000");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Permissions-Policy: camera=(), microphone=(), geolocation=()");

X-Frame-Options: SAMEORIGIN एक बहुत important header है — यह आपके page को किसी दूसरे site के iframe में load होने से रोकता है। Clickjacking attacks में attacker आपके page को invisible iframe में load करके user को trick करता है। यह एक line का code है लेकिन एक पूरी category of attacks से बचाता है।

Strict-Transport-Security header browser को force करता है कि यह site हमेशा HTTPS से ही खुले। एक बार यह header set हो जाए तो browser automatically HTTP requests को HTTPS पर redirect करता है — server तक request पहुंचने से पहले ही।

Favicon का Issue — और उसका Permanent Fix

Favicon एक छोटी सी चीज है लेकिन branding के लिए important है। हमारे case में server पर favicon.svg था लेकिन PHP page में properly नहीं दिख रहा था। कभी-कभी hosting providers file paths differently handle करते हैं।

इसका permanent solution हमने inline SVG data URI से किया जो किसी external file पर depend नहीं करता — directly PHP variable में store होता है और हर condition में काम करता है:

= "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%235b4fff'/%3E%3C/svg%3E";

Theme color meta tag mobile browsers में address bar का color change करता है — एक छोटी सी detail जो professional feel देती है और brand identity को reinforce करती है।

Performance — Database Indexing जरूर करें

जैसे-जैसे posts बढ़ती हैं, queries slow हो सकती हैं अगर database indexing नहीं की गई। यह एक ऐसी चीज है जो नए developers अक्सर ignore करते हैं और बाद में पछताते हैं।

-- Slug पर index — सबसे ज्यादा use होने वाली query
ALTER TABLE blog_posts ADD INDEX idx_slug (slug);

-- Status और date पर index — listing के लिए
ALTER TABLE blog_posts ADD INDEX idx_status_date (status, created_at DESC);

-- Category के लिए
ALTER TABLE blog_posts ADD INDEX idx_category (category);

Index का मतलब है database एक shortcut map बना लेता है। Without index, हर query पूरी table scan करती है — 10,000 posts हों तो seconds लग सकते हैं। With index, directly relevant rows तक microseconds में पहुंचा जा सकता है।

Deployment Checklist — Go-Live से पहले जरूर Check करें

जब भी आप ऐसा custom system live करें, यह checklist follow करें — एक भी point miss मत करो:

  1. display_errors = Off रखो production में। Error messages में database names, file paths और code structure दिखता है जो hackers के लिए goldmine है।
  2. Database credentials strong और unique रखो। Same password कई जगह use मत करो।
  3. Admin password हमेशा bcrypt hash से store करो — plain text बिल्कुल नहीं।
  4. Uploads folder में PHP execution blocked करो .htaccess से।
  5. HTTPS enable करो — Hostinger और cPanel पर free SSL available है।
  6. Directory listing disable करो (Options -Indexes)।
  7. config.php web accessible न हो — .htaccess से block करो।
  8. Regular backups — Database और files दोनों के। Automated backup setup करो।

निष्कर्ष — AI के साथ Code करना कैसा रहा?

Claude AI के साथ काम करना एक experienced developer के साथ pair programming जैसा था। वो सिर्फ code नहीं देता — समझाता भी है कि यह code क्यों लिखा जा रहा है, क्या risks हैं, और better alternatives क्या हो सकते हैं।

जब bugs आए तो AI ने directly live page fetch करके problem diagnose की — यह impressive था। Bug fix भी systematic था — पहले root cause, फिर fix, फिर verification।

कुछ mistakes जरूर हुईं — एक bug fix करते हुए नया bug create हुआ। लेकिन यह भी real development का हिस्सा है। Important यह है कि हर bug से कुछ सीखा।

अगर आप भी अपना custom blog system बनाना चाहते हैं, तो शुरुआत करें। Perfect का इंतजार मत करो। एक simple version बनाओ, use करो, problems सामने आएंगी, fix करो, और system better होता जाएगा।

CodeCraft पर आप PHP, MySQL, और web development से जुड़े सभी topics के free interactive courses पा सकते हैं — Live editor के साथ, Hindi और English दोनों में।