---
title: "Turn SEO requests into engineering tasks you can build, measure, and test"
description: "What SEO needs from engineering: Core Web Vitals, rendering, structured data, canonicals, and CI checks, with code and clear lines for when to push back."
canonical: "https://scalewithsearch.com/articles/technical-seo-for-developers"
date: "2026-03-20"
modified: "2026-09-25"
---
## Site navigation

- [Scale With Search](https://scalewithsearch.com/)
- Real estate
  - Real estate
    - [Real estate](https://scalewithsearch.com/for/real-estate)
- Work
  - Start here
    - [Send your brief](https://scalewithsearch.com/work#send-your-brief)
    - [Prepare your six-question brief](https://scalewithsearch.com/work#prepare-your-six-question-brief)
  - Build
    - [Site build, content library with SEO, signal desk](https://scalewithsearch.com/work)
- For your business
  - Trades and home services
    - [Auto body and collision shops](https://scalewithsearch.com/for/auto-body-and-collision-shops)
    - [Foundation and home repair contractors](https://scalewithsearch.com/for/foundation-and-home-repair)
    - [Garage door and fencing contractors](https://scalewithsearch.com/for/garage-door-and-fencing-contractors)
    - [HVAC contractors](https://scalewithsearch.com/for/hvac-contractors)
    - [Janitorial and commercial cleaning companies](https://scalewithsearch.com/for/janitorial-and-commercial-cleaning)
    - [Locksmiths](https://scalewithsearch.com/for/locksmiths)
    - [Moving companies](https://scalewithsearch.com/for/moving-companies)
    - [Pest control companies](https://scalewithsearch.com/for/pest-control-companies)
    - [Plumbing and electrical contractors](https://scalewithsearch.com/for/plumbing-and-electrical-contractors)
    - [Restoration and water or fire damage companies](https://scalewithsearch.com/for/restoration-and-water-fire-damage)
    - [Roofing companies](https://scalewithsearch.com/for/roofing-companies)
    - [Towing companies](https://scalewithsearch.com/for/towing-companies)
    - [Tree services and landscaping companies](https://scalewithsearch.com/for/tree-services-and-landscaping)
    - [Solar installers](https://scalewithsearch.com/for/solar-installation)
    - [General contractors](https://scalewithsearch.com/for/general-contractors-and-construction)
    - [Paving, concrete, and flooring contractors](https://scalewithsearch.com/for/paving)
  - Practices and professional services
    - [Bookkeeping and tax practices](https://scalewithsearch.com/for/bookkeeping-and-tax-practices)
    - [Dental practices](https://scalewithsearch.com/for/dental-practices)
    - [Family and criminal defense law firms](https://scalewithsearch.com/for/family-and-criminal-defense-law-firms)
    - [Med spas and aesthetics practices](https://scalewithsearch.com/for/med-spas-and-aesthetics)
    - [Personal injury law firms](https://scalewithsearch.com/for/personal-injury-law-firms)
    - [Veterinary clinics](https://scalewithsearch.com/for/veterinary-clinics)
    - [Gyms and fitness studios](https://scalewithsearch.com/for/fitness)
    - [Therapy and outpatient health practices](https://scalewithsearch.com/for/therapy-and-outpatient-health)
    - [Medical billing companies](https://scalewithsearch.com/for/medical-billing)
    - [Insurance agencies](https://scalewithsearch.com/for/insurance-agencies)
    - [Financial advisors](https://scalewithsearch.com/for/financial-advisors)
    - [Property management companies](https://scalewithsearch.com/for/property-management)
    - [Recruiting and staffing agencies](https://scalewithsearch.com/for/recruiting-and-staffing)
    - [Architects and interior designers](https://scalewithsearch.com/for/architects-and-interior-designers)
    - [Logistics and supply chain companies](https://scalewithsearch.com/for/logistics-and-supply-chain)
  - Agencies, MSPs, and manufacturing
    - [IT and managed service providers](https://scalewithsearch.com/for/it-and-managed-service-providers)
    - [Machine shops and precision manufacturers](https://scalewithsearch.com/for/machine-shops-and-precision-manufacturing)
    - [Marketing agencies and freelancers](https://scalewithsearch.com/for/marketing-agencies-and-freelancers)
    - [SEO agencies and consultants](https://scalewithsearch.com/for/seo-agencies-and-consultants)
    - [Small manufacturers and fabricators](https://scalewithsearch.com/for/small-manufacturers-and-fabricators)
  - Restaurants, shops, studios, and nonprofits
    - [Restaurants and hospitality businesses](https://scalewithsearch.com/for/restaurants-and-hospitality)
    - [Retail stores and ecommerce sellers](https://scalewithsearch.com/for/retail-and-ecommerce)
    - [Photographers, event planners, and travel agents](https://scalewithsearch.com/for/photographers)
    - [Churches and nonprofits](https://scalewithsearch.com/for/churches-and-nonprofits)
  - [All industries](https://scalewithsearch.com/for/)
- Learn
  - For your office
    - [Office job guides](https://scalewithsearch.com/guides/)
    - [Browser calculators](https://scalewithsearch.com/tools/)
  - Start here
    - [How it works](https://scalewithsearch.com/how-it-works)
    - [Free Starter Kit](https://scalewithsearch.com/kit/business-memory-starter-kit.zip)
    - [Synthetic specimen](https://scalewithsearch.com/specimen/working-session-specimen.zip)
  - Guides
    - [The Complete Guide to Business Memory for AI Agents](https://scalewithsearch.com/articles/business-memory-for-ai-agents-guide)
    - [The Complete Small-Business Guide to AI Agent Governance](https://scalewithsearch.com/articles/ai-agent-governance-guide-small-business)
    - [The Complete Guide to Leaving Vendor AI Memory](https://scalewithsearch.com/articles/leaving-vendor-ai-memory-guide)
  - Articles by cluster
    - [Business memory](https://scalewithsearch.com/articles/business-memory-for-ai-agents-guide)
    - [Agent governance](https://scalewithsearch.com/articles/ai-agent-governance-guide-small-business)
    - [Migration and ownership](https://scalewithsearch.com/articles/leaving-vendor-ai-memory-guide)
  - For machines
    - [llms.txt](https://scalewithsearch.com/llms.txt)
    - [llms-full.txt](https://scalewithsearch.com/llms-full.txt)
    - [Machine view](https://scalewithsearch.com/?view=machine)
- Company
  - Evidence
    - [Proof](https://scalewithsearch.com/proof)
  - Company
    - [About](https://scalewithsearch.com/about)

# Turn SEO requests into engineering tasks you can build, measure, and test.

Marketing sends you a ticket: "Improve SEO." Nothing in the ticket tells you what code to change.

Most of what SEO needs from engineering fits in three areas: performance, machine-readable markup, and crawlable architecture. Each area has measurable outputs. This article translates each one into tasks with code and pass criteria, so that the next vague ticket becomes a scoped one.

## Know what engineering controls

Content, keywords, and links belong to the marketing team. These three areas belong to you:

| Area | What Google needs | What you measure |
|---|---|---|
| Performance | Pages that load fast and respond to input | LCP, INP, and CLS from real users |
| Structured data | Facts in a format a machine can parse | Valid JSON-LD with no errors in the Rich Results Test |
| Crawlability | Every public page reachable through plain links | Status codes, redirect hops, rendered HTML, canonical targets |

Some tickets do not map to one of these rows. For those, ask the requester which search result or report the ticket should change.

## Measure Core Web Vitals in production

Core Web Vitals are three metrics that Google collects from real Chrome users:

| Metric | Measures | Good at the 75th percentile |
|---|---|---|
| Largest Contentful Paint (LCP) | When the main content appears | 2.5 seconds or less |
| Interaction to Next Paint (INP) | Delay between input and the next frame | 200 milliseconds or less |
| Cumulative Layout Shift (CLS) | Unexpected layout movement | 0.1 or less |

INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. FID measured only the first input's delay. INP measures the latency of interactions across the whole visit, so JavaScript that was acceptable under FID can fail INP.

Lab data and field data answer different questions. Lighthouse runs one load on one simulated device, which is good for regression tests. Field data comes from real visits, and Google uses the field data. Collect field data yourself with the `web-vitals` library:

```js
import { onCLS, onINP, onLCP } from 'web-vitals';

function send(metric) {
  navigator.sendBeacon('/rum', JSON.stringify({
    name: metric.name,
    value: metric.value,
    page: location.pathname,
  }));
}

onCLS(send);
onINP(send);
onLCP(send);
```

Group the results by page template. A slow product template affects every product page, so fix templates, not URLs. [Debugging Core Web Vitals](/articles/core-web-vitals-debugging) covers how to trace each metric to its cause.

### Fix layout shift from dynamic content

Most CLS comes from three sources. Fix each one at the template level.

1. Set `width` and `height` on every `img` and `video` element. The browser then reserves the correct box before the file loads.
2. Reserve space for content that loads late, such as ads, embeds, and cookie banners. Use `min-height` or `aspect-ratio` on the container.
3. Control font swaps. Preload the main web font and use a fallback font with matched metrics, through `size-adjust` in `@font-face`. Or use `font-display: optional` so that a late font does not swap in.

### Reduce input delay for INP

INP fails when the main thread is busy at the moment of input. Long tasks, which run over 50 milliseconds, are the usual cause.

- Split large bundles with dynamic `import()`, so that code for hidden features does not run on load.
- Load third-party scripts with `async` or `defer`. Audit each tag with the marketing team and remove the ones nobody reads.
- Break long event handlers into smaller tasks. Update the UI first, then yield before the heavy work. In Chromium browsers, `scheduler.yield()` yields directly. Use `setTimeout` as the fallback elsewhere.
- Mark scroll and touch listeners `{ passive: true }` when they do not call `preventDefault()`.

## Choose a rendering mode by page type

Googlebot renders JavaScript with a current version of Chromium. Rendering happens in a separate step after the crawl, and it can wait in a queue. Content in the initial HTML response is indexed without that wait.

| Mode | Initial HTML holds the content | Use it for |
|---|---|---|
| Static generation (SSG) | Yes | Marketing pages, docs, blog posts |
| Server-side rendering (SSR) | Yes | Product and category pages with changing data |
| Client-side rendering (CSR) | No | Authenticated dashboards and account pages |

For every page that must rank, use SSR or SSG. Keep CSR for pages behind a login, which search engines cannot reach anyway. [Server-side rendering for SEO](/articles/server-side-rendering-seo) compares framework options.

### Keep hydration consistent

In React, Vue, and similar frameworks, the client hydrates the server HTML. Render the same content on the server that appears on the client. If the server sends a placeholder and the client fetches the real text, Google may index the placeholder. Treat hydration mismatch warnings as bugs.

### Know when client-side rendering removes pages from search

CSR causes three failures:

- **Render delay**: the content is absent until the render step runs, so new pages take longer to index.
- **Content mismatch**: the initial HTML and the rendered page disagree, for example on the title or canonical tag.
- **Link discovery failure**: navigation built from `onClick` handlers without an `a` element and an `href` gives Googlebot no URL to follow.

One case removes a page completely. If the initial HTML carries `noindex` in the robots meta tag, Google does not render the page. JavaScript that removes the tag later has no effect.

## Implement structured data as JSON-LD

Structured data describes the page in the Schema.org vocabulary. Google supports JSON-LD, Microdata, and RDFa, and recommends JSON-LD. JSON-LD sits in one block, separate from the HTML structure, so a template redesign does not break it.

Place the JSON in a `script` element of type `application/ld+json`, in the page `head` or `body`:

```json
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Turn SEO requests into engineering tasks",
  "datePublished": "2026-03-20",
  "dateModified": "2026-09-25",
  "author": { "@type": "Organization", "name": "Example Co" }
}
```

Generate the block from the same data that renders the page. Hand-written JSON-LD drifts from the visible content, and Google requires the markup to match what users see. [JSON-LD for developers](/articles/structured-data-jsonld-developers) covers framework patterns.

Test markup before deploy in three places:

1. Run the page through the Schema Markup Validator at `validator.schema.org` for vocabulary errors.
2. Run it through the Google Rich Results Test for eligibility and required properties.
3. After release, watch the enhancement reports in Search Console for new errors.

## Automate SEO checks in CI

Manual audits catch regressions weeks late. Put the measurable parts into the pipeline.

### Run Lighthouse in GitHub Actions

Lighthouse CI runs Lighthouse against a preview build and fails the job when an assertion fails. A `lighthouserc.json` with budgets looks like this:

```json
{
  "ci": {
    "collect": { "url": ["http://localhost:3000/"], "numberOfRuns": 3 },
    "assert": {
      "assertions": {
        "categories:seo": ["error", { "minScore": 0.9 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        "total-blocking-time": ["warn", { "maxNumericValue": 300 }]
      }
    }
  }
}
```

A lab test has no real user input, so Lighthouse cannot measure INP. Total Blocking Time is the lab metric that tracks it most closely. [Lighthouse in GitHub Actions](/articles/lighthouse-github-actions-seo) gives the full workflow file.

### Detect broken canonicals before merge

A canonical tag tells Google which URL is the main version of a page. Three rules catch most errors: the URL is absolute, it returns `200`, and a canonical page points to itself. A small script checks the first two:

```js
// check-canonicals.mjs
const base = process.env.PREVIEW_URL;
const paths = ['/', '/pricing', '/blog/example-post'];

for (const path of paths) {
  const url = new URL(path, base).href;
  const html = await (await fetch(url)).text();
  const match = html.match(/<link[^>]+rel="canonical"[^>]+href="([^"]+)"/i);
  if (!match) throw new Error(`${url}: no canonical tag`);
  const canonical = match[1];
  if (!canonical.startsWith('https://')) throw new Error(`${url}: canonical is not absolute`);
  const res = await fetch(canonical, { redirect: 'manual' });
  if (res.status !== 200) throw new Error(`${url}: canonical returns ${res.status}`);
}
```

To check the third rule, run the same match on the canonical URL's HTML and compare the values. The regular expression expects `rel` before `href`. For a production check, parse the HTML with a real parser.

## Push back on requests that break accessibility

Some SEO requests make the page worse for people. Refuse these and explain the reason in the ticket. [SEO and accessibility conflicts](/articles/seo-accessibility-conflicts-developers) covers more cases.

### Keywords in alt text

Alt text exists so that screen-reader users get the content of an image. WCAG success criterion 1.1.1 requires a text alternative that serves the same purpose. Google's own image guidance also warns against keyword stuffing in `alt`. Write alt text that describes the image. Give decorative images an empty `alt=""`.

### Every heading as an H1

Keep one H1 per page and a logical H2 and H3 hierarchy. Google has said that the number of H1 elements does not affect ranking. The reason for a clean hierarchy is structure. Screen-reader users navigate by heading level. WCAG success criterion 1.3.1 requires that structure to be programmatic.

### Hidden text for crawlers

Text that is visible to crawlers but hidden from users violates Google's spam policies. Screen readers may also read it aloud, which confuses their users.

When an SEO request conflicts with WCAG, the accessibility requirement wins. It is often a legal requirement, and Google's guidance rarely asks for the conflicting change.

----

```text
                  .|########||.                                       .|########||.                                       .|########||.
               |##||.      .||##|.                                 |##||.      .||##|.                                 |##||.      .||##|.
             |#|.              .|#|.                             |#|.              .|#|.                             |#|.              .|#|.
           |#|                    |#|                          |#|                    |#|                          |#|                    |#|
         .#|                        |#.                      .#|                        |#.                      .#|                        |#.
        .#.                          .#|                    .#.                          .#|                    .#.                          .#|
       |#.                            .#|                  |#.                            .#|                  |#.                            .#|
      |#             ......             #|                |#             ......             #|                |#             ......             #|
     .#           ||#########|           #|              .#           ||#########|           #|              .#           ||#########|           #|
    .#.         |######||######|.        .#.            .#.         |######||######|.        .#.            .#.         |######||######|.        .#.
    #.        .##|###|##|#|######|        .#            #.        .##|###|##|#|######|        .#            #.        .##|###|##|#|######|        .#
   ||        |##|#||||||||||||#||#|        ||          ||        |##|#||||||||||||#||#|        ||          ||        |##|#||||||||||||#||#|        ||
   #        |#||||||||||||||||||||#|        #.         #        |#||||||||||||||||||||#|        #.         #        |#||||||||||||||||||||#|        #.
  ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||
  #       .#||||||||||||||||||||||||#|       #        #       .#||||||||||||||||||||||||#|       #        #       .#||||||||||||||||||||||||#|       #
 ||   ....|||#||||||##|#|||#|#||##||||....|. ||      ||   ....|||#||||||##|#|||#|#||##||||....|. ||      ||   ....|||#||||||##|#|||#|#||##||||....|. ||
 #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#      #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#      #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#
 #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.     #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.     #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.
.|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||    .|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||    .|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||
||   |....  #. ....|#.      |. ...|. ....||   ||    ||   |....  #. ....|#.      |. ...|. ....||   ||    ||   |....  #. ....|#.      |. ...|. ....||   ||
#.  .||||||##||||||##||####|||||||#||||||#|   .#    #.  .||||||##||||||##||####|||||||#||||||#|   .#    #.  .||||||##||||||##||####|||||||#||||||#|   .#
#    .||####|###########################|.     #    #    .||####|###########################|.     #    #    .||####|###########################|.     #
#      .#||||||.#.|| # |. ..# #| #|||||#|      #    #      .#||||||.#.|| # |. ..# #| #|||||#|      #    #      .#||||||.#.|| # |. ..# #| #|||||#|      #
#      .#|||||| ..  |# ## |#| ...#|#|||#|      #    #      .#|||||| ..  |# ## |#| ...#|#|||#|      #    #      .#|||||| ..  |# ## |#| ...#|#|||#|      #
#      .#|||#|# .# .#| #| ##|.#..#|||||#|      #    #      .#|||#|# .# .#| #| ##|.#..#|||||#|      #    #      .#|||#|# .# .#| #| ##|.#..#|||||#|      #
#      .#||||||############|######|||||#|      #    #      .#||||||############|######|||||#|      #    #      .#||||||############|######|||||#|      #
#   |...||#||||||#||||#|#||||||#|||||#||| |.|  #    #   |...||#||||||#||||#|#||||||#|||||#||| |.|  #    #   |...||#||||||#||||#|#||||||#|||||#||| |.|  #
#. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#    #. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#    #. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#
|| |  ...||  ...##| |  #| .|. |. #####  .. .| ||    || |  ...||  ...##| |  #| .|. |. #####  .. .| ||    || |  ...||  ...##| |  #| .|. |. #####  .. .| ||
|| ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||    || ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||    || ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||
.# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.    .# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.    .# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.
 #.|||||||#############################| ||| .#      #.|||||||#############################| ||| .#      #.|||||||#############################| ||| .#
 ||       ##||#||#||||||#||#||#||#||##.      ||      ||       ##||#||#||||||#||#||#||#||##.      ||      ||       ##||#||#||||||#||#||#||#||##.      ||
  #       .#|||||||||#||#|||||||||||#|       #        #       .#|||||||||#||#|||||||||||#|       #        #       .#|||||||||#||#|||||||||||#|       #
  ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||
  .#        |#||||||||||||||||||||#|        #.        .#        |#||||||||||||||||||||#|        #.        .#        |#||||||||||||||||||||#|        #.
   ||        |#||||||||||||||||||#|        ||          ||        |#||||||||||||||||||#|        ||          ||        |#||||||||||||||||||#|        ||
    #.        .######|#|#########|        .#            #.        .######|#|#########|        .#            #.        .######|#|#########|        .#
    .#.         |#####||||#####|         .#.            .#.         |#####||||#####|         .#.            .#.         |#####||||#####|         .#.
     |#           ||########||           #.              |#           ||########||           #.              |#           ||########||           #.
      |#             ......             #|                |#             ......             #|                |#             ......             #|
       |#.                            .#|                  |#.                            .#|                  |#.                            .#|
        |#.                          .#.                    |#.                          .#.                    |#.                          .#.
         .#|                        |#.                      .#|                        |#.                      .#|                        |#.
           |#|                    |#|                          |#|                    |#|                          |#|                    |#|
            .|#|.              .|#|.                            .|#|.              .|#|.                            .|#|.              .|#|.
               |##||.      .||##|                                  |##||.      .||##|                                  |##||.      .||##|
                 .||########||.                                      .||########||.                                      .||########||.

Scale With Search  2026  [scalewithsearch.com](https://scalewithsearch.com)
```
