---
title: "Anatomy of a great SEO ticket — Technical SEO Audit Workshop"
description: "Why most SEO recommendations are never implemented Developers receive vague requests like \"fix canonicals\" or \"improve site speed\". They cannot estimate…"
url: https://optimizeall.com/learn/technical-seo-audit-in-practice/anatomy-of-a-great-seo-ticket
updated: 2026-10-05
---

Technical SEO Audit Workshop · Writing tickets developers act on · lesson 9 of 14 · 13 min

# Anatomy of a great SEO ticket

## Why most SEO recommendations are never implemented

Developers receive vague requests like "fix canonicals" or "improve site speed". They cannot estimate, test or close such tickets, so the work stalls. A good ticket reads like a small specification: what is wrong, where, why it matters, exactly what should change, and how to prove it is done.

## The ticket template

```text
Title:          [SEO] <Component>: <specific change>
Type / Priority: Bug | Improvement — P1/P2/P3 (from roadmap score)
Summary:        One or two sentences a non-SEO can understand.
Business impact: Why it matters, linked to revenue templates/markets.
Current behaviour: What happens now, with 2–5 example URLs.
Expected behaviour: Precise rule, including edge cases.
Acceptance criteria: Testable statements (Given/When/Then or checklist).
How to test:    Commands, tools, URLs to verify.
Out of scope:   What not to change.
Dependencies / risks: Related tickets, rollback notes.
References:     Google documentation links, evidence IDs.
```

## Example ticket 1: cross-market canonicals (RC1)

```text
Title: [SEO] Product & blog templates: canonical must self-reference the current market URL

Summary: Product and journal pages in /en-pk/, /en-ae/ and /ar-ae/ currently set their
canonical to the /en-gb/ equivalent. Google therefore treats non-UK pages as duplicates
and shows UK pages (with GBP prices) to users in Pakistan and the UAE.

Business impact: Affects all product and journal URLs in three markets that account for
most revenue (per GA4). Evidence: E-08, F-03, F-11.

Current behaviour:
  /en-ae/products/brass-lantern-large → canonical: /en-gb/products/brass-lantern-large

Expected behaviour:
  - The canonical equals the page's own absolute URL: protocol https, host www, current
    market folder, no query string, lowercase, trailing slash rule as per site standard.
  - Parameterised variants (?utm_*, ?variant=) canonicalise to the clean URL of the same market.
  - hreflang tags continue to list all four markets plus x-default (no change).

Acceptance criteria:
  [ ] For every product and journal URL in each market, rendered HTML contains exactly one
      canonical link element in the head, equal to its own clean URL.
  [ ] Canonical is present in the server-rendered (raw) HTML, not only after hydration.
  [ ] No canonical points to a URL that redirects or returns non-200.
  [ ] Automated test added to the template test suite.

How to test:
  curl -s https://staging.kiranhome.example/en-ae/products/brass-lantern-large | grep -i canonical
  Crawl staging product list (list provided) and check canonical = address.

Out of scope: Changing URL structure, hreflang values.
Risk: Low. Rollback = revert template commit.
```

## Example ticket 2: facets (RC2)

```text
Title: [SEO] Category filters: stop exposing crawlable parameter links; block low-value parameters

Expected behaviour:
  1. Filter options for size, price, sort and multi-select combinations update the listing
     without outputting crawlable anchor links to parameter URLs (use buttons with JS
     state or URL fragments). Brand and colour filters for the approved list (attached)
     continue to link to clean static URLs, e.g. /en-gb/collections/cushions/indigo/.
  2. Parameter order is normalised: brand, colour, size, price.
  3. Zero-result filter combinations return HTTP 404.
  4. robots.txt adds (after ticket SEO-12 confirms no approved facets use these parameters):
       Disallow: /*?*sort=
       Disallow: /*?*price=
       Disallow: /*?*size=
       Disallow: /*/search
Acceptance criteria:
  [ ] Rendered category HTML contains no anchor href with sort=, price= or size= parameters.
  [ ] Approved facet pages return 200, self-canonical, unique title/H1.
  [ ] /en-gb/collections/cushions/?size=xl&price=0-10 with no results returns 404.
  [ ] robots.txt changes deployed only after staging tests pass.
```

## Writing good acceptance criteria

Acceptance criteria must be **binary** (pass/fail) and **checkable** by a QA engineer without SEO knowledge:

- Good: "Returns HTTP 404 for zero-result combinations."
- Bad: "Handle empty filters properly."
- Good: "The LCP image on category templates has no loading=lazy attribute and has fetchpriority=high."
- Bad: "Improve LCP."

## Give developers the why — briefly

Link to Google's documentation for context (canonicalisation, faceted navigation, redirects) and keep the explanation short. Developers respect specificity; long SEO essays inside tickets are skipped.

## Sizing and splitting

If a ticket would take more than a sprint, split it by template or by market. Tickets that can ship independently get value live sooner and are easier to roll back.

## Example ticket 3: CDN bot rule (RC8), written as Gherkin

Some teams prefer acceptance criteria as Given/When/Then scenarios that QA can automate:

```gherkin
Feature: Search and AI search crawlers can reach public pages
  Scenario Outline: Verified crawler requests a public product page
    Given a request from a verified <bot> IP address
    And the User-Agent header contains "<token>"
    When it requests "/en-ae/products/brass-lantern-large/"
    Then the response status is 200
    And no challenge or CAPTCHA page is served

    Examples:
      | bot            | token          |
      | Googlebot      | Googlebot      |
      | Bingbot        | bingbot        |
      | OAI-SearchBot  | OAI-SearchBot  |
      | PerplexityBot  | PerplexityBot  |

  Scenario: Protected paths stay protected
    Given any automated client
    When it requests "/account/" or "/checkout/"
    Then bot protection rules still apply
```

Pair it with the written AI crawler policy (which bots are allowed on which paths) so the rule is a business decision, not an SEO whim.

## Worked example 2: rewriting a vague request

A Lahore agency (illustrative) receives back a ticket its junior wrote: "Fix hreflang issues on the site." The developer asked five questions and parked it. Rewritten: title "[SEO] Product template: hreflang set must be complete and reciprocal"; three example URLs with current output; expected behaviour listing the four market codes plus `x-default`, absolute URLs, self-reference, and targets that return 200 and are self-canonical; acceptance criteria as a checklist plus a crawl-based test; out of scope: journal templates (separate ticket). It was estimated and shipped in the next sprint.

## Common mistakes

- Tickets without example URLs.
- Expected behaviour that ignores edge cases (parameters, trailing slashes, uppercase, pagination).
- Mixing several unrelated fixes into one ticket.
- Not specifying whether a tag must be in the raw HTML or can be added client-side.

## Video lecture: Anatomy of a great SEO ticket

Lecture coming soon · 13 chapters · about 8 minutes. Read the full transcript below.

1. Anatomy of a great SEO ticket
2. The ticket template
3. Ticket 1: RC1 canonicals
4. Ticket 1: acceptance criteria
5. Ticket 2: RC2 facets
6. Ticket 3: RC8 in Gherkin
7. Good acceptance criteria
8. Why, briefly; size and split
9. Example 2: rewriting a vague ticket (illustrative)
10. Common mistakes
11. Watch me do it: writing the RC8 ticket
12. Raw HTML or client-side?
13. Recap and try this now

## Lecture transcript

### Anatomy of a great SEO ticket

Most SEO recommendations are never implemented. Not because developers don't care, but because they receive requests like fix canonicals or improve site speed. You can't estimate that, test it or close it. In this lecture you'll learn the anatomy of a ticket developers actually act on: a template, worked tickets for Kiran Home's biggest root causes, how to write binary acceptance criteria, including Given-When-Then scenarios, and how to size and split work.

### The ticket template

A good ticket reads like a small specification. What's wrong, where, why it matters, exactly what should change, and how to prove it's done. The template in the lesson text has a title in the form SEO, component, specific change. Type and priority from your roadmap score. A one or two sentence summary a non-SEO can understand. Business impact. Current behaviour with two to five example URLs. Expected behaviour, including edge cases. Acceptance criteria. How to test. Out of scope. Dependencies and risks. And references to documentation and evidence IDs.

### Ticket 1: RC1 canonicals

Let's look at ticket one, for root cause one. Title: product and journal templates, canonical must self-reference the current market URL. Summary: pages in the Pakistan and UAE folders set their canonical to the UK equivalent, so Google treats them as duplicates and shows UK pages with pound prices. Current behaviour, with an example URL. Expected behaviour: the canonical equals the page's own absolute URL, https, www, current market folder, no query string, lowercase, and the site's trailing slash rule. Parameter variants canonicalise to the clean URL of the same market. hreflang unchanged.

### Ticket 1: acceptance criteria

The acceptance criteria for ticket one are binary. For every product and journal URL in each market, the rendered HTML contains exactly one canonical link in the head, equal to its own clean URL. The canonical is present in the server-rendered raw HTML, not only after hydration. No canonical points to a URL that redirects or returns non-two-hundred. And an automated test is added to the template test suite. Then how to test: a curl command against staging, and a list crawl checking canonical equals address.

### Ticket 2: RC2 facets

Ticket two, for facets. Expected behaviour: filter options for size, price, sort and multi-select combinations update the listing without outputting crawlable anchor links; brand and colour filters on the approved list keep clean static URLs. Parameter order is normalised. Zero-result combinations return four-oh-four. And robots.txt adds disallow rules for the low-value parameters and search, but only after another ticket confirms no approved facets use them. Acceptance: no anchor hrefs with those parameters in the rendered HTML, approved facet pages self-canonical with unique titles, and zero-result combos returning four-oh-four.

### Ticket 3: RC8 in Gherkin

Ticket three, for the CDN bot rule, is a great place to use Given-When-Then scenarios, which QA teams can automate. Given a request from a verified bot's IP, and a user agent containing its token, when it requests a public product URL, then the response is two hundred and no challenge page is served. The examples table lists Googlebot, Bingbot, OAI-SearchBot and PerplexityBot. And a second scenario confirms protected paths like account and checkout stay protected. Pair it with the written AI crawler policy, so the rule is a business decision, not an SEO whim.

### Good acceptance criteria

What makes acceptance criteria good? They're binary, pass or fail, and checkable by a QA engineer without SEO knowledge. Good: returns HTTP four-oh-four for zero-result combinations. Bad: handle empty filters properly. Good: the LCP image on category templates has no loading lazy attribute and has fetchpriority high. Bad: improve LCP. If a tester can't tell whether it passed, it isn't an acceptance criterion.

### Why, briefly; size and split

Give developers the why, briefly. Link to Google's documentation on canonicalisation, faceted navigation or redirects, and keep the explanation short. Developers respect specificity; long SEO essays inside tickets get skipped. And size and split. If a ticket would take more than a sprint, split it by template or by market. Tickets that ship independently deliver value sooner and are easier to roll back.

### Example 2: rewriting a vague ticket (illustrative)

Worked example two, illustrative. A Lahore agency's junior writes a ticket: fix hreflang issues on the site. The developer asks five questions and parks it. Rewritten, it becomes: product template, hreflang set must be complete and reciprocal. Three example URLs with current output. Expected behaviour listing the four market codes plus x-default, absolute URLs, self-reference, and targets that return two hundred and are self-canonical. Acceptance criteria as a checklist plus a crawl-based test. Out of scope: journal templates, in a separate ticket. It was estimated and shipped the next sprint.

### Common mistakes

Common mistakes. Tickets without example URLs. Expected behaviour that ignores edge cases like parameters, trailing slashes, uppercase and pagination. Mixing several unrelated fixes into one ticket. And not specifying whether a tag must be in the raw HTML or can be added client-side. Each of these leads to questions, delays or a fix that doesn't work for Google.

### Watch me do it: writing the RC8 ticket

Watch me do it. I'll write the RC eight ticket from scratch in ten minutes. Step one: the title. Square brackets SEO, CDN bot rules: allow verified search and AI search crawlers on public paths. Step two: the summary, in plain language. Since the relaunch, Cloudflare challenges Bingbot and some AI search crawlers on product pages, so Bing dropped many products and AI assistants can't read them. Step three: business impact, with evidence IDs E twelve and E thirteen. Step four: current behaviour, with two example URLs and a firewall event screenshot. Step five: expected behaviour. Verified Googlebot, Bingbot, OAI-SearchBot and PerplexityBot receive two hundreds on public paths; account and checkout stay protected; training crawlers follow the written policy. Step six: acceptance criteria, as the Gherkin scenarios from the lesson, including the protected-paths scenario. Step seven: how to test: filter firewall events for verified bots after deploy, and confirm zero challenges on public paths over forty-eight hours. Step eight: out of scope: changes to rate limiting for abusive bots. Step nine: risk and rollback: low; revert the rule. I link the AI crawler policy, and I ask the developer to estimate it rather than guessing myself.

### Raw HTML or client-side?

A word on where the tag must live, because it's the most common source of back-and-forth. For anything search engines must see reliably, like canonicals, robots meta, hreflang and structured data, specify that it must be in the server-rendered raw HTML. Client-side injection adds rendering risk and can create raw versus rendered mismatches, and remember that a noindex in raw HTML may stop Google rendering at all. Say it explicitly in the expected behaviour and in the acceptance criteria. Developers can't guess this requirement, and many frameworks make the client-side path the easy default.

### Recap and try this now

Recap. Write tickets as small specifications: specific title, plain-language summary, business impact, current versus expected behaviour with examples and edge cases, binary acceptance criteria, how to test, scope and risks. Use Given-When-Then when QA will automate. Try this now. Take the vaguest SEO request in your backlog and rewrite it with the template, including at least three example URLs and four binary acceptance criteria.

## Key takeaways

- A great ticket states current vs expected behaviour with example URLs and precise rules.
- Acceptance criteria must be binary and testable by someone without SEO knowledge.
- Specify edge cases and whether output must exist in the raw server-rendered HTML.
- Split large work by template or market so value ships sooner and rollback is easier.

## Try it

Rewrite a vague SEO recommendation you have seen (e.g. 'fix redirects') into a complete ticket using the template.

- [Previous: Prioritising with impact × effort × confidence](https://optimizeall.com/learn/technical-seo-audit-in-practice/prioritising-impact-effort-confidence)
- [Next: Working with development teams: sprints, QA and release risk](https://optimizeall.com/learn/technical-seo-audit-in-practice/working-with-development-teams)
- [All lessons of Technical SEO Audit Workshop](https://optimizeall.com/learn/technical-seo-audit-in-practice)
