A mobile app with no website only has one way to be found: the app store search box. I wanted a second one, so HairOver got a real website.
This post is the build history of hairover.app, taken from the repository's git log. The first commit was on August 24, 2026. The last one I'm covering was on September 23. That's 29 commits over 30 days (24 if you don't count merge commits).
What happened, commit by commit
| Date | What changed |
|---|---|
| Aug 24 | First commit, the entire first landing page, a build fix, then rebranding assets |
| Aug 30 | Landing page revamp, SEO-optimized content, image uploads, and the switch to the hairover.app domain |
| Aug 31 – Sep 2 | Deployment config changes, landing page revamped again, a shared container across all pages |
| Sep 7 | Hero and feature sections rebuilt |
| Sep 10 | Mobile fix for the hero video mask, web manifest image |
| Sep 13 | OG image fix, then a Journal section (the blog) |
| Sep 16 – 17 | "White revamp": a light theme, then light and dark mode (37 files, ~1,700 lines added) |
| Sep 22 | UI fixes, SEO fixes, and a blog pagination/count bug |
| Sep 23 | Sitemap update |
Looking at that table, the landing page was rebuilt at least three times in its first two weeks (Aug 24, Aug 30, Sep 1), and the hero was rebuilt a fourth time on Sep 7. That wasn't planned. I kept looking at the page and thinking it didn't explain the product fast enough.
Why I made these decisions
Why Astro
The site is marketing and content: landing page, feature pages, hairstyle guides, face-shape guides, a journal. It has almost no interactivity. Astro sends static HTML by default, which is what you want when the whole point of the site is to be crawled and to load fast on a phone.
The stack is small on purpose:
- Astro (static output) with content collections for hairstyles, face shapes, and journal posts
- Tailwind CSS
- @astrojs/sitemap for the XML sitemap
- Vercel Web Analytics, with custom events for the clicks that matter
Why the site is mostly guides, not features
There are four feature pages (face and hair analysis, try-on, recommendations, the style library). Everything else is content:
- 12 journal posts, like Hairstyles for Round Faces, Best Haircuts for a Receding Hairline, How to Take a Photo for Hairstyle Recommendations
- 6 face-shape guides: oval, round, square, heart, oblong, diamond
- 6 hairstyle pages: pixie cut, curtain bangs, textured crop, layered lob, classic fade, soft waves
The reasoning: almost nobody searches for "AI hairstyle app HairOver." Lots of people search for haircuts for a round face right before they book an appointment. That's exactly when HairOver is useful. So the content goes where the searches are, and every guide leads back to the same action: try it on your own photo.
One post title sums up the product's view: Face Shape Is a Starting Point, Not a Rule. Guides can narrow down the options. Only a try-on shows you the result on your face.
What I built into it
A few details that took real time and are easy to miss:
Hand-maintained lastmod dates in the sitemap. The sitemap plugin can't tell the difference between "I rewrote this page" and "I tweaked a shared component." So the config has a hand-kept map of real content-change dates. Pages without an entry leave lastmod out instead of showing a made-up date. There's also a small build hook that copies sitemap-0.xml to /sitemap.xml, so the conventional URL works.
Crawl headers. Every page is served with X-Robots-Tag: index, follow, max-image-preview:large…, and the 404 page with noindex, follow. Trailing slashes are consistent across Astro and Vercel, and the apex domain redirects to www.
llms.txt. A generated /llms.txt gives AI assistants a plain-text summary of the product, key facts, and where the guides are. Every response also links to it with a Link: rel="describedby" header. It even includes a caution I think belongs there: treat recommendations as a reference for a stylist, not a guaranteed salon result.
WebMCP tools. When the browser supports it, the site registers three tools an AI agent can call: set_color_theme, open_google_play, and open_hairstyle_guide. This is a bet on where browsing is going, not something I expect to drive installs today.
Analytics events tied to the funnel. The tracked events are google_play_click, app_store_click, hairstyle_category_click, feature_click, faq_open, and cta_click. I didn't want a pageview counter. I wanted to see whether a guide reader clicks through to Google Play.
What worked
I can only answer this about the build, not about results. The site is live, crawlable, has a real sitemap, works in light and dark mode, and every page points at one conversion: the Google Play listing.
What didn't work (yet)
- I rebuilt the landing page too many times before any real traffic reached it. Each rebuild felt productive. None of them was tested against a user.
- The App Store button is a placeholder. The site is ready for iOS; the app isn't.
- I don't have traffic or install numbers to show here. The events exist, but I haven't published what they say. Until I do, this is a well-built site whose impact is unknown.
What I learned
Building the website was the comfortable part. I know how to ship a fast, well-structured static site. That's exactly why it's easy to spend too long on it: it feels like progress on distribution without actually being distribution.
A crawlable site is just a place people can land. Getting them there is a separate problem.
What I'm testing next
- Whether the face-shape and hairstyle guides get search impressions, and on which queries
- The click-through rate from guide pages to
google_play_click - Whether hairstyle-intent searches (like "haircuts for a round face") turn into installs at all, or whether the app gets found some other way
I don't know the answer to any of these yet.
Key takeaways
- hairover.app: 29 commits between Aug 24 and Sep 23, 2026, on Astro, Tailwind, and Vercel Analytics.
- The site is built around hairstyle search intent, not feature pages.
- Small technical details matter for SEO: honest
lastmod, crawl headers, consistent trailing slashes,llms.txt. - Rebuilding the landing page felt like progress, but only real traffic can show whether it worked.