Engineering
Technical SEO for Web Applications: What Actually Moves Rankings
Most SEO advice is written for content sites. Applications fail differently, and usually for reasons no keyword tool will ever show you.
When a web application does not rank, the cause is rarely the content. It is that the crawler received an empty page, or found four URLs holding the same thing, or spent its crawl allowance on faceted filter combinations, or was told by a stray directive not to index the page at all. None of these appear in a keyword report, and all of them need someone who can change the code.
Start with what the crawler actually receives
The single most useful habit in technical SEO is to stop looking at the page in a browser. Fetch the raw HTML the server returns and read that. It is routinely a different document from the one you see.
For client-rendered applications it may be an empty shell with a script tag. Google does render JavaScript, but rendering is queued separately from crawling and is not guaranteed to be prompt or complete - and other crawlers, including the ones behind social previews and several AI systems, render far less reliably or not at all.
The fix is to serve the content in the initial HTML for any page you want ranked. Server-side rendering, static generation or prerendering all achieve this; which one you choose is an architecture decision, but the requirement is not negotiable if search matters.
Duplicate URLs are the quiet killer
Applications generate URL variants effortlessly: tracking parameters, session identifiers, sort and filter combinations, trailing-slash variants, uppercase paths, and the same page reachable from two routes. Each variant is a separate URL to a crawler, and the ranking signals split between them.
Three things resolve it. Every page needs a self-referencing canonical pointing at the version you want indexed. The server should redirect variants - non-canonical host, wrong protocol, inconsistent trailing slash - with a permanent redirect rather than serving all of them. And parameters that do not change the content should not produce a distinct indexable URL.
The most common mistake here is a canonical that disagrees with the sitemap, or a canonical pointing at a URL that redirects. Both are treated as confused signals, and confusion resolves against you.
Crawl budget is real once you have filters
For a fifty-page site, crawl budget is not your problem. For an application with faceted navigation it is one of the first problems, because a handful of filters produce a combinatorial explosion of URLs and a crawler will happily spend weeks on them instead of on the pages that matter.
Decide explicitly which filter combinations deserve to be indexed - usually the ones with genuine search demand, such as a category plus one attribute - and make the rest non-indexable. Use robots.txt to stop crawling of parameter patterns that have no value, and a noindex directive for pages that must remain crawlable for their links but should not appear in results.
Those two are not interchangeable, and confusing them is common: a page blocked in robots.txt cannot be crawled, so its noindex is never seen, and the URL can still appear in results on external signals alone. If you want a page out of the index, it must be crawlable and carry noindex.
Core Web Vitals, measured in the field
Performance is a ranking factor, and more importantly it is a conversion factor. The mistake is optimising against a lab score on a fast machine instead of against what real users experience.
For Largest Contentful Paint the usual culprits are an image that is not preloaded, a font that blocks text from painting, and a render-blocking stylesheet. Preload the element that will be largest, use font-display so text appears immediately in a fallback, and inline the styles needed for the first screen.
Cumulative Layout Shift almost always comes from images and embeds without dimensions, from content injected above existing content, and from web fonts that reflow on swap. Set explicit width and height, reserve space for anything that arrives late, and match fallback font metrics.
Interaction to Next Paint is the one applications fail most, because it measures responsiveness under a heavy JavaScript main thread. Break up long tasks, do less work on the main thread, and be honest about how much framework is genuinely needed on a page whose job is to display information.
Structured data that reconciles
Structured data does not directly raise rankings, but it determines how a result is displayed and how confidently a search engine resolves what your site is about. The common failure is not invalid markup - it is markup that contradicts itself across pages, describing the same organisation three different ways.
Use one identified organisation node and have every other block reference it, rather than restating the company on each page. Validate in the build rather than by pasting into a testing tool occasionally, so a regression fails the build instead of quietly shipping.
Do not mark up reviews you have not collected, ratings you have aggregated from nothing, or FAQs that do not appear on the page. These attract manual actions, and the rich result is not worth the risk.
Internationalisation, where it usually goes wrong
If you serve multiple languages, hreflang tells search engines which version to show. It is also one of the easiest things to get subtly wrong.
- Annotations must be reciprocal. If the English page points at the German one, the German page must point back, or the whole set is ignored.
- Every URL in the set must return 200. One 404 in the cluster undermines it.
- Each page's canonical must point at itself, not at the English version. A canonical to another language is a request to drop that page from the index.
- Use x-default for the fallback when no language matches.
- Do not machine-translate a page and mark it as a language alternate unless it is genuinely good. A thin or mangled translation is worse than not offering that language at all.
Measurement that survives a privacy-first web
A large share of visitors block client-side analytics, and the share is highest among technical audiences - which for a software product is the audience you most want to understand. Numbers from a blocked tag are not just incomplete, they are biased in a direction that matters.
Server logs and server-side measurement do not have this problem, and they also show you crawler behaviour, which client-side analytics never will. For a new site, watching Googlebot arrive in the logs is a more useful signal than any dashboard - it tells you whether you have a crawling problem or a ranking problem, which are fixed very differently.
Search Console remains the most valuable source, because it reports the queries you already appear for. Those queries are a better content brief than any keyword tool, because they show where you are close enough that a small improvement changes the outcome.
A practical order of work
- Verify indexability firstSearch Console, coverage report, and a crawl of your own site. There is no point optimising a page that is excluded.
- Fix renderingEnsure the content you want ranked is in the initial HTML response.
- Resolve duplicatesCanonicals, redirects and parameter handling, so signals concentrate rather than split.
- Control crawlDecide which generated URLs deserve indexing and stop the rest consuming crawl allowance.
- Then performanceField data, not lab scores, and fixed in the build pipeline so it cannot regress.
- Then contentWritten against the queries Search Console shows you are already close on.
That order matters. Content written for a page the crawler cannot see is wasted work, and performance tuning on a page that is excluded from the index changes nothing at all.