<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dhillon</title>
    <description>The latest articles on DEV Community by Dhillon (@dhillon_bafeab426dca2fc89).</description>
    <link>https://dev.to/dhillon_bafeab426dca2fc89</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4221864%2Fe6d1c1cd-1e94-4b22-9360-b793faf4b0b5.png</url>
      <title>DEV Community: Dhillon</title>
      <link>https://dev.to/dhillon_bafeab426dca2fc89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dhillon_bafeab426dca2fc89"/>
    <language>en</language>
    <item>
      <title>I built a free video downloader as a solo dev. Here is what I learned about SEO the hard way</title>
      <dc:creator>Dhillon</dc:creator>
      <pubDate>Mon, 02 Nov 2026 09:12:02 +0000</pubDate>
      <link>https://dev.to/dhillon_bafeab426dca2fc89/i-built-a-free-video-downloader-as-a-solo-dev-here-is-what-i-learned-about-seo-the-hard-way-271n</link>
      <guid>https://dev.to/dhillon_bafeab426dca2fc89/i-built-a-free-video-downloader-as-a-solo-dev-here-is-what-i-learned-about-seo-the-hard-way-271n</guid>
      <description>&lt;p&gt;I'm a solo developer from Bangladesh, and this year I built &lt;a href="https://sstik.top" rel="noopener noreferrer"&gt;SSTIK&lt;/a&gt;, a free web tool that saves public TikTok, Instagram, Facebook and X videos (HD, no TikTok watermark, MP3 for sounds). It now has a website in 15 languages, an Android app and a Telegram bot.&lt;/p&gt;

&lt;p&gt;Building the downloader was the fun part. Getting people to find it was harder. Here are the things that actually mattered.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack, briefly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; static HTML pages (one per platform and language) plus a small JavaScript file, hosted on Vercel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs:&lt;/strong&gt; a Node service and a Python service that read the public post and return the available video and audio files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data:&lt;/strong&gt; MongoDB for anonymous usage stats and settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android app:&lt;/strong&gt; plain Java, small, with share-to-download, a player and a few video tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing fancy. Static pages load fast and are easy for search engines to read, which turned out to matter more than anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: a title change can cost you most of your traffic overnight
&lt;/h2&gt;

&lt;p&gt;Almost all of my Google traffic came from people searching the brand name, "sstik". In one big redesign I changed the home page title from&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SSTIK - Video Downloader | ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to a keyword-first title that put the brand at the very end.&lt;/p&gt;

&lt;p&gt;The next day my average position for "sstik" went from about &lt;strong&gt;4&lt;/strong&gt; to about &lt;strong&gt;30&lt;/strong&gt;, and impressions fell from ~2,000 a day to ~100. Search Console showed the exact day.&lt;/p&gt;

&lt;p&gt;If most of your clicks are navigational (people looking for &lt;em&gt;you&lt;/em&gt;), keep the brand at the start of the title. I've moved it back to &lt;code&gt;SSTIK - TikTok Downloader No Watermark + Instagram, FB &amp;amp; X&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: redirect every old URL
&lt;/h2&gt;

&lt;p&gt;The same redesign renamed pages like &lt;code&gt;/facebook-downloader&lt;/code&gt; to &lt;code&gt;/facebook-video-downloader&lt;/code&gt; and simply deleted the old ones. They returned 404. On Vercel the fix is a few lines in &lt;code&gt;vercel.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/facebook-downloader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/facebook-video-downloader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"permanent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also had &lt;code&gt;www.sstik.top&lt;/code&gt; serving the full site instead of redirecting, so Google split clicks between two hosts. A host-based redirect fixed that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/:path*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"www.sstik.top"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://sstik.top/:path*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permanent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lesson 3: 65 pages in the sitemap does not mean 65 pages in Google
&lt;/h2&gt;

&lt;p&gt;Search Console said: 6 indexed, 57 "Discovered, currently not indexed". Translating every page into 14 languages made the site bigger, not more valuable. Google knew the pages existed and simply didn't bother crawling them yet.&lt;/p&gt;

&lt;p&gt;What I'm doing instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing genuinely useful guides (for example &lt;a href="https://sstik.top/guides/tiktok-to-mp3" rel="noopener noreferrer"&gt;TikTok to MP3&lt;/a&gt; and &lt;a href="https://sstik.top/guides/save-video-to-iphone-gallery" rel="noopener noreferrer"&gt;saving videos to iPhone Photos&lt;/a&gt;) in the two languages where I actually get impressions, English and Bengali.&lt;/li&gt;
&lt;li&gt;Linking to them from inside the relevant sections of the main pages, not only from the footer.&lt;/li&gt;
&lt;li&gt;Adding &lt;code&gt;HowTo&lt;/code&gt;, &lt;code&gt;FAQPage&lt;/code&gt; and &lt;code&gt;BreadcrumbList&lt;/code&gt; structured data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 4: automate the sitemap so you never forget it
&lt;/h2&gt;

&lt;p&gt;I kept forgetting to update &lt;code&gt;lastmod&lt;/code&gt; in &lt;code&gt;sitemap.xml&lt;/code&gt;. Now a GitHub Action does it on every push: a short Python script rebuilds the sitemap from the HTML files, uses the last git commit date of each file as &lt;code&gt;lastmod&lt;/code&gt;, commits it if anything changed, and sends only the changed URLs to &lt;a href="https://www.indexnow.org/" rel="noopener noreferrer"&gt;IndexNow&lt;/a&gt; so Bing and others recrawl them quickly.&lt;/p&gt;

&lt;p&gt;The core of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;git_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;log&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--format=%cs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                         &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember &lt;code&gt;fetch-depth: 0&lt;/code&gt; in &lt;code&gt;actions/checkout&lt;/code&gt;, otherwise every file gets today's date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;More guides, a few honest directory listings, and patience while Google re-crawls. If you've recovered from a similar ranking drop, I'd love to hear what worked for you in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SSTIK only works with public posts and doesn't store videos. Please download only your own videos or ones you have permission to use.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
