14
13 Comments

SEO from a Newbie for Beginners

So, you've got a bootstrap project, an awesome product, but few customers. My time has come to dive into SEO.
Running a bootstrap or indie project really sharpens a lot of skills and teaches you to focus more on business (money). Plus, you realize that marketers aren't just sitting around—they've got a tough job too!

What follows is a pretty basic rundown of things I've learned and done over the last few days (or weeks).

First Things First: The Domain

They say .com is slightly better, but overall, it doesn't really matter. If your domain isn't brand new, that's even better—domain reputation and backlinks matter! This is also why buying underperforming but promising projects (including domains) can pay off if you know what you're doing.

We snagged aso.dev for a magical $98 (or close to that).

aso.dev

Next, you build the site.
Since SEO is a priority, server-side rendering (SSR) is a must. There's a lot of talk about Google being able to parse JavaScript, but that's mostly nonsense. Sometimes it can, but it generally hates doing so, so don’t risk it. You could use a heavy SSR setup for React or Angular, but we went with astro.build—super fast, simple, and elegant. They have plenty of free and complex themes, but we settled on their Starlight theme after tweaking the home page a bit (you need to copy the Hero.astro component and override Head.astro).

Starlight

I'm loving it so far. We're even reworking the site for our player, meows.app, because dynamic rendering based on API responses works perfectly. We hired a junior dev to rewrite it from scratch on the same tech stack for cheap—great practice for him and a faster, simpler site for us. Angular 14 just isn't cutting it anymore, even with Universal rendering (feels like ages ago)—check out our WIP example with Astro.

It’s looking pretty good. Previously, we used tinypng for compressing images (manually or through API + GPT script), but now we're sticking to the built-in image optimization tools in astro.build.

SEO performance in Google Chrome for ASO.dev

Google Search Console

Next up: Google Search Console. Add your site to track indexing, spot errors, and unlock achievements (which you can flex on Twitter).

Achivements

Google Search Console

Google Search Console

There are plenty of tools out there, but for now, I’m sticking with a couple of free ones.

Ahrefs

User login - Ahrefs

Ahrefs dashboard

Ahrefs dashboard

Ahrefs is great because you get 10k site queries and a detailed analysis of your pages’ issues. You can’t fix everything, but reducing errors helps a lot.

This tool provided most of the insights I’ve used to improve my site.

Fixes and Improvements

Optimizing Page Titles and Descriptions

Titles should be 50-60 characters, and descriptions 110-160. In the meta structure of my .md files, I added them like this:

seo:
  seo_title: "All-in-One ASO Solution for iOS Developers, marketing"
  seo_description: "ASO.dev is ultimate tool for App Store Optimization (ASO) with App Store Connect integration.Manage,optimize,grow your apps effortlessly with powerful features"

Then I asked GPT to write a Bash script to check these files. After about 30 tries, it finally worked (still faster than if I did it by hand):

# Function to check the length of seo_title and seo_description
check_seo_params() {
  local file=$1
  local in_seo_block=false
  local seo_title=""
  local seo_description=""

  while IFS= read -r line
  do
    # Look for the start of the seo block
    if [[ "$line" =~ ^seo: ]]; then
      in_seo_block=true
    fi

    # If inside the seo block, search for seo_title and seo_description
    if [[ "$in_seo_block" = true ]]; then
      # Search for seo_title
      if [[ "$line" =~ seo_title:[[:space:]]*\"(.*)\" ]]; then
        seo_title="${BASH_REMATCH[1]}"
      fi
      # Search for seo_description
      if [[ "$line" =~ seo_description:[[:space:]]*\"(.*)\" ]]; then
        seo_description="${BASH_REMATCH[1]}"
      fi
    fi

    # If the seo block ends (new block or end of file), stop reading
    if [[ "$in_seo_block" = true && "$line" =~ ^[^[:space:]] && ! "$line" =~ ^seo ]]; then
      in_seo_block=false
    fi
  done < "$file"

  local have_errors=false

  # Check if seo_title is present and valid
  if [[ -z "$seo_title" ]]; then
    echo $divider
    echo $file
    echo "seo_title: Empty or not found"
    have_errors=true
  elif [[ ${#seo_title} -lt 50 || ${#seo_title} -gt 60 ]]; then
    echo $divider
    echo $file
    echo "seo_title: Length ${#seo_title} (50 <> 60): '${seo_title}'"
    have_errors=true
  fi

  # Check if seo_description is present and valid
  if [[ -z "$seo_description" ]]; then
    if [[ $have_errors = false ]]; then
      echo $divider
      echo $file
    fi
    echo "seo_description: Empty or not found"
    have_errors=true
  elif [[ ${#seo_description} -lt 110 || ${#seo_description} -gt 160 ]]; then
    if [[ $have_errors = false ]]; then
      echo $divider
      echo $file
    fi
    echo "seo_description: Length ${#seo_description} (110 <> 160): '${seo_description}'"
    have_errors=true
  fi

  # Print divider only if there are no errors
  # if [[ $have_errors = false ]]; then
  #   echo $divider
  # fi
}
echo $divider
# Recursive search for all .md and .mdx files in the src/content/docs directory
find src/content/docs -type f \( -name "*.md" -o -name "*.mdx" \) | while read file; do
  # Check if the file contains a seo block before proceeding
  if grep -q "seo:" "$file"; then
    check_seo_params "$file"
  fi
done
echo $divider

Run the script, navigate to the file, copy the text into GPT, and ask for the optimal title and description. Here’s an example prompt:

Write seo_title and seo_description, send the result in English, use best practices and length requirements for seo.
Result in the format:
`yaml
  seo_title: ""
  seo_description: ""
`
seo_title 50-60 symbols, seo_description 100-160 symbols
text is ...

Next, update all pages—super basic, I know, but better than having no metadata or duplicate content.

Favicon Fixes

We messed up the favicon a bit—used realfavicongenerator to generate and test.

OG Tags

I also added OG tags—meta tags that make your links look better in social media previews. At the very least, include og:title, og:description, and og:image (use an absolute path). All our images are served via bunny.net, but I’m still fine-tuning that setup.

application/ld+json

Added some structured data with application/ld+json—no idea if it works, but it seems cool. Check out structured data markup.

 {
        tag: "script",
        attrs: {
            type: "application/ld+json",
        },
        content: JSON.stringify({
            "[@context](/context)": "https://schema.org",
            "[@type](/type)": "WebSite",
            url: canonical?.href,
            headline: ogTitle,
            description: page_description_seo,
            image: [imageUrl?.href],

            mainEntity: {
                "[@type](/type)": "Article",
                headline: page_description_seo,
                url: canonical?.href,
                dateModified: data?.lastUpdated,
                image: [imageUrl?.href],
                author: {
                    "[@type](/type)": "Organization",
                    name: "ASO.dev",
                    url: "https://aso.dev",
                },
                publisher: {
                    "[@type](/type)": "Organization",
                    name: "ASO.dev",
                    logo: {
                        "[@type](/type)": "ImageObject",
                        url: fileWithBase(config.favicon.href),
                    },
                },
            },
        }),
    },

Custom 404 Page

We built a custom 404 page. Defaulting to the index page is considered an error and could hurt SEO.

Setting the noindex meta tag on the 404 page:

// 404
if (canonical?.pathname === "/404") {
    headDefaults.push({
        tag: "meta",
        attrs: {
            name: "robots",
            content: "noindex",
        },
    });
}

Setting up a custom 404 page in Nginx config:

 location / {
            proxy_redirect off;
            absolute_redirect off;

            proxy_set_header Host $http_host;

            try_files $uri $uri/ =404;

            # try_files $uri $uri/ /index.html;
            add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
            add_header Pragma "no-cache";
            add_header Expires "Thu, 01 Jan 1970 00:00:00 GMT";
        }
        # 404 page
        error_page 404 /404.html;
        location = /404.html {
            root   /app;
            internal;
        }

Redirects

We had a bunch of 301 redirects. We made sure all URLs ended with a trailing slash (e.g., https://aso.dev/aso/ instead of https://aso.dev/aso) to avoid duplication. After a thorough review, we eliminated outdated links in the code and added old URL redirects via NGINX.

  # https://aso.dev/app-info/app-info/ https://aso.dev/aso/app-info/
  rewrite ^(/ru|/en)?/app-info/app-info/?$ $1/aso/app-info/ permanent;

hreflang

We added x-default to the hreflang attribute. Didn't know that was a thing until recently.

// Link to language alternates.
if (canonical && config.isMultilingual) {
    for (const locale in config.locales) {
        const localeOpts = config.locales[locale];
        if (!localeOpts) continue;
        const langPostfix = localeOpts.lang === "en" ? "" : localeOpts.lang;
        headDefaults.push({
            tag: "link",
            attrs: {
                rel: "alternate",
                hreflang: localeOpts.lang,
                href: localizedUrl(canonical, langPostfix).href,
            },
        });
    }
    headDefaults.push({
        tag: "link",
        attrs: {
            rel: "alternate",
            hreflang: "x-default",
            href: localizedUrl(canonical, '').href,
        },
    });
}

SEMrush

SEMrush

SEMrush

I’ve been using SEMrush longer—it’s easier to downgrade to a free plan, but 100 checks and their pricing aren’t great.

Backlinks

Getting backlinks is tricky. You need links from reputable sites—spammy backlinks will hurt you. One good link from the New York Times beats hundreds from random blogs. We’re building up our backlink profile by listing our site on startup and indie project platforms. We found an Excel sheet with hundreds of link opportunities and are slowly working through it.

Launching on Product Hunt can help—subscribe for updates. We've delayed the launch a few times, but it’s coming soon.

Also, we try to write genuinely useful articles, not just filler.

I might have missed something or gotten things wrong — feel free to share in the comments!

posted to Icon for group Developers
Developers
on October 25, 2024
  1. 1

    I think it’s even clearer now how much it matters not just to get the technical side right (SSR, redirects, proper meta), but also to create content with real value and find ways to attract clean backlinks. It’s interesting that more and more people in the SEO space are talking about a “blended strategy” – meaning you shouldn’t limit yourself to rankings or CTR, but look at the entire business pipeline, mentions, and brand searches. That’s exactly the point Matt Bertram keeps stressing in his podcast: don’t treat SEO as just a checklist, but as an integrated growth strategy.

  2. 2

    In Ahrefmy website has "Page has no outgoing links" Error do you think it could be due to no Sitemap.xml? Is sitemap important for SEO?

    1. 2

      The “Page has no outgoing links” error in Ahrefs usually means that the specific page doesn’t link to any external websites. This isn’t directly related to having a sitemap.xml file, but let’s clarify both aspects:

      1.	Outgoing Links: Not having any external (outgoing) links can be seen as a missed opportunity because external links help search engines understand the context and relevance of your content. It’s generally good SEO practice to include a few high-quality, relevant external links where it makes sense.
      2.	Sitemap Importance: A sitemap.xml is crucial for SEO because it helps search engines discover and index all your important pages, especially if your website is new, has a lot of content, or has pages that are not easily accessible through internal linking. It doesn’t directly fix the “no outgoing links” error, but it ensures that search engines can crawl your site more effectively.
      

      In short, fixing the “no outgoing links” error involves adding relevant external links, and having a sitemap.xml helps with better indexing and crawling but won’t solve this specific issue on its own.

      1. 1

        thanks for your reply.

  3. 2

    Still in the development stage of My App, these tips will help. Thanks!

    1. 2

      Glad you found the tips helpful! Best of luck with your app development—excited to see it come to life! 🚀

  4. 2

    Those are some good insights, I launched my first Micro Sass product today. I will definitely try to implement these strategies

    1. 2

      That’s awesome! 🎉
      Congrats on launching your first micro SaaS product!
      Taking those first steps is a huge achievement. I’m glad you found the insights helpful! Starting with some solid SEO basics will definitely give your product more visibility in the long run. Keep experimenting, and don’t hesitate to reach out if you have questions or need feedback as you implement these strategies. Here’s to more launches and growth ahead! 🚀

  5. 1

    Your blog provides such useful webdev tips! EchoAPI’s API testing features have let me save a lot of time, making it easier to ensure my application works as intended.

  6. 1

    I believe that AI progress will change SEO business significantly in the foreseeable future, unfortunatelly.

  7. 1

    Although you have don a great work but it's not completely helpful for me. I want to remove the glitch from my webpage calculadora de porcentagem and I have not get any solution yet. Because I am beginner in SEO and don't now so much about this issue.

  8. 0

    It sounds like you’ve done a fantastic job diving into SEO for a new project, Igor! Starting with a strong domain and going with SSR for better SEO performance is smart. Using Astro for its speed and simplicity, Especially with the Starlight theme, seems like an excellent choice for keeping things manageable while ensuring a fast-loading site. Google Search Console, Ahrefs, and SEMrush are also core tools, so it’s great you’re leveraging these.

    Your improvements, like optimizing meta tags, adding OG tags, and implementing structured data (even if it feels experimental), will enhance the site’s SEO and social sharing. The Bash script for checking title and description lengths is a nice touch—automation like that can save so much time. Adding a custom 404 page with noindex, setting up proper redirects, and hreflang with x-default for multilingual support are also solid moves that many skip but make a difference.

    For backlinks, focusing on quality over quantity is key. Listing on startup directories, launching on Product Hunt, and contributing genuinely useful content will boost visibility without risking spammy links. Overall, you’ve covered a lot of ground! Keep testing and iterating—SEO is an ongoing process, but your foundation is strong.

Trending on Indie Hackers
Your SaaS Isn’t Failing — Your Copy Is. User Avatar 57 comments Solo SaaS Founders Don’t Need More Hours....They Need This User Avatar 45 comments Planning to raise User Avatar 15 comments The Future of Automation: Why Agents + Frontend Matter More Than Workflow Automation User Avatar 13 comments From side script → early users → real feedback (update on my SaaS journey) User Avatar 11 comments AI Turned My $0 Idea into $10K/Month in 45 Days – No Code, Just This One Trick User Avatar 7 comments