I recently built iGram.pk, an Instagram downloader that supports 13 languages, serves 9 dedicated tool pages, and runs on a single index.php front controller with zero framework dependencies. Here's what I learned about routing, i18n, SEO, and shipping fast without Laravel or Symfony.
Why No Framework?
The project needed to be:
- Fast to load — every millisecond counts for a tool people use on mobile
- Cheap to host — Heroku free/basic tier with Apache
- Simple to deploy —
git pushand done
A framework would add 20-50MB of vendor code, boot time overhead, and complexity that a tool site doesn't need. The entire codebase fits in a handful of files.
The Routing Problem
With 9 tool pages, 13 languages, 40+ blog posts, and static pages (about, contact, FAQ, legal), I needed clean URLs like:
/download-instagram-reels (English) /ar/download-instagram-reels (Arabic) /fr/story-saver (French) /blog (Blog index) /how-to-download-instagram-videos (Blog post) The solution is a single index.php that acts as a front controller. Apache's .htaccess sends everything there:
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /index.php [L,QSA] Then index.php parses the URI, extracts the language prefix (if any), and routes to the correct PHP file:
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $requestUri = rawurldecode($requestUri); $slug = ltrim($requestUri, '/'); // Extract language prefix $languages = ['ar', 'de', 'es', 'fr', 'id', 'it', 'ja', 'pl', 'pt', 'ru', 'tr', 'vi']; $currentLang = 'en'; foreach ($languages as $l) { if (strpos($slug, $l . '/') === 0) { $currentLang = $l; $slug = substr($slug, strlen($l) + 1); break; } } // Route to the right file $toolPages = ['story-saver', 'downloadgram', 'download-instagram-reels', ...]; if (in_array($slug, $toolPages)) { include __DIR__ . '/pages/tools/' . $slug . '.php'; exit; } No regex router library, no route registration — just an array lookup. It handles hundreds of URL combinations with zero overhead.
Multi-Language Without a Framework
The i18n system is file-based. Each language gets a PHP file that returns an associative array:
// includes/translations/ar.php return [ 'common' => [ 'download' => 'تحميل', 'paste_link' => 'الصق رابط Instagram هنا', 'video_downloader' => 'تحميل فيديو', // ... ], 'story-saver' => [ 'title' => 'حفظ ستوري انستقرام - تحميل القصص | iGram.pk', 'description' => '...', 'steps' => ['...', '...', '...'], ], ]; The loader merges common translations with page-specific ones:
function loadTranslations($lang, $slug = null) { if ($lang === 'en') return []; $all = include __DIR__ . '/translations/' . $lang . '.php'; $common = $all['common'] ?? []; $page = $all[$slug] ?? []; return array_merge($common, $page); } English is the default — if a translation key is missing, the template falls back to the hardcoded English string using the null coalescing operator:
$pageTitle = $t['title'] ?? 'Instagram Story Saver - Download Stories | iGram.pk'; This approach is simple, fast (no database queries), and easy to extend. Adding a new language is literally creating one PHP file.
SEO Architecture
For a content site competing in search, SEO structure matters as much as content quality. Here's what I implemented:
Structured Data (Schema.org)
Every page type gets appropriate JSON-LD markup through helper functions:
function schemaArticle($headline, $description, $path, $published, $modified) { return [ '@context' => 'https://schema.org', '@type' => 'Article', 'headline' => $headline, 'description' => $description, 'datePublished' => $published, 'dateModified' => $modified, 'author' => ['@type' => 'Organization', 'name' => 'iGram.pk'], // ... ]; } Tool pages use WebApplication schema, blog posts use Article, FAQ sections use FAQPage. Each schema type is a function that takes the page's actual data, so the markup always matches the visible content.
Hreflang for Multi-Language
Each tool page generates hreflang tags for all 13 language variants:
foreach (getSupportedLanguages() as $l) { $hreflangPages[$l] = ltrim(langUrl($l, $slug), '/'); } This tells Google which version to show French users vs Arabic users vs English users. Without hreflang, Google might index the wrong language variant for a given region.
Blog Posts Are English-Only
Blog content is only in English. If someone hits /fr/how-to-download-instagram-videos, the router 301 redirects to the English version. This avoids thin translated content and keeps the canonical clear.
if ($currentLang !== 'en') { header('Location: /' . $blogSlug, true, 301); exit; } AI Search Optimization
With ChatGPT, Perplexity, and Google AI Overviews reshaping how people find tools, I added signals that AI crawlers specifically look for:
llms.txt
This is a structured file at the site root that tells AI crawlers what the site is about:
# iGram.pk > iGram.pk is a free, web-based Instagram downloader... ## Tools - [Video Downloader](https://igram.pk/download-instagram-video): download videos in HD - [Story Saver](https://igram.pk/story-saver): save Stories before expiry ... robots.txt AI Bot Permissions
Explicitly allowing AI crawler bots:
User-agent: GPTBot Allow: / User-agent: ClaudeBot Allow: / User-agent: PerplexityBot Allow: / Many sites block these bots. Allowing them increases the chance your tool gets recommended in AI-generated answers.
Performance on Heroku
The entire app runs on Heroku's basic tier with Apache:
web: vendor/bin/heroku-php-apache2 With Cloudflare in front for caching and SSL, the setup handles the traffic without issues. PHP's built-in opcache handles the rest — no Redis, no Memcached, no queue workers.
Total infrastructure cost: basically free.
What I'd Do Differently
Start with more tool pages — dedicated pages for each content type (video, Reels, photos, carousel, Stories, Highlights, profile photos) rank independently in search. I should have built all 9 from day one instead of starting with 3.
Schema markup from the beginning — I added
FAQPageandArticleschema later. Having it from launch would have earned rich snippets sooner.Content velocity matters — writing 40+ blog posts in the first two weeks had a measurable impact on organic traffic. Each post is a new entry point from search.
Results
Within the first week:
- 1,500+ daily clicks from organic search
- 143,000+ impressions for branded queries
- Ranking on page 1 for multiple "igram" keyword variations
- 9 tool pages, 43 blog posts, 13 language variants
The site is at igram.pk if you want to see the implementation live. The blog has all 43 guides and tutorials.
Takeaways
- You don't need a framework for a tool site. Plain PHP with a front controller is fast, simple, and deployable anywhere.
- Multi-language support with file-based translations is underrated. It's faster than database lookups and easier to maintain than JSON files.
- SEO is architecture, not just content. Clean URLs, hreflang, structured data, and proper canonicalization compound over time.
- Ship fast, iterate based on search console data. GSC shows you exactly what people search for — write content that matches those queries.
Built with PHP, deployed on Heroku, cached with Cloudflare. No framework, no build step, no node_modules.

