# Page removal checklist

Version: 1.0
Updated: 2026-10-02
Method: https://suhailmohebi.com/notes/the-page-you-deleted-is-still-being-served/

Purpose: retire a URL everywhere it is still claimed, not only at your origin.

Output: one removal record per URL, closed by a dated check from outside your infrastructure.

## Use when

You are deleting, merging or redirecting a page, and search results, feeds, previews or caches may keep its old claim alive.

## Quick start

1. Pick one end state per URL (section 1).
2. Clear the surfaces you own, then report the old URL as well as the new one (sections 2–3).
3. Run the outside check, then recheck the result page until the old claim is gone (section 4).

A redirect fixes the destination, never the claim. Removal ends when the old result stops being served.

## 1. Decide the end state for each URL

| End state | Choose it when | Avoid it when |
|---|---|---|
| 410 Gone | the removal is deliberate and permanent | a page replaces it |
| 404 Not Found | the page is gone and you cannot say whether for good | a page replaces it |
| 301 or 308 to a successor | one page clearly meets the same need | the target is only the home page or loosely related |
| `noindex`, still served | people still need the URL but search should drop it | the URL is blocked in robots.txt |

- Google treats 404 and 410 alike and drops either on recrawl; 410 also tells every client the removal is intentional.
- Crawlers must fetch the URL to see the new status or `noindex`, so never block a removed URL in robots.txt.
- 302 and 307 are temporary, weaker signals: use them only if the page may return.

## 2. Clear the surfaces you own

Three surfaces keep the old truth: the index (the old headline and summary), your own feeds and lists, and third parties (caches, aggregators, answer engines). Start with your own: a URL your site still lists is a current claim that the page exists.

- [ ] Internal links, navigation, canonical and `hreflang` tags
- [ ] XML sitemaps and RSS, Atom or JSON feeds
- [ ] Structured data (JSON-LD lists, breadcrumbs)
- [ ] Site search index and prebuilt search data
- [ ] `llms.txt`, Markdown copies and other machine-readable lists
- [ ] Social preview tags (`og:url`, `og:image`) pointing at the URL or its assets
- [ ] Redirect rules: one hop to the successor, no chains, nothing redirecting into the old URL
- [ ] Generator inputs, so the next build cannot list the URL again

```bash
# Read-only: leftover mentions of the old path in source and output.
grep -rnF --exclude-dir=.git "/old-page/" /work/site
```

## 3. Notify about the old URL, not only the new one

Submission is part of the deletion. Search Console and Webmaster Tools need a verified site.

| Channel | Use it to | Limit |
|---|---|---|
| Google Search Console, Removals | hide a result that must stop now | about six months; lasting only with 404, 410, `noindex` or a password |
| IndexNow | report old and new URLs to participating engines (Bing among them) | Google does not participate; 200 or 202 means received |
| Bing Webmaster Tools | hide now (Block URLs) or ask for a recrawl (Submit URLs) | a block lasts 90 days unless extended |
| CDN or edge cache | purge the old URL and its assets | reaches no engine and no third-party cache |
| Link previews | re-fetch it in each platform’s debugger (Meta: Sharing Debugger) | posts already shared can keep the old card |
| Pages you do not own | Google Refresh Outdated Content tool; Bing Content Removal Tool | only once the page is gone or changed |

Old 404 pages drop out as Google recrawls them; keep Removals for claims that must stop now.

IndexNow request (it notifies engines; send it on purpose):

```text
POST https://api.indexnow.org/indexnow
Content-Type: application/json; charset=utf-8

{"host": "example.com", "key": "<key>",
 "urlList": ["https://example.com/old-page/", "https://example.com/new-page/"]}
```

Key: 8–128 characters (a–z, A–Z, 0–9, hyphen), served as UTF-8 text at `https://example.com/<key>.txt` containing only the key; up to 10,000 URLs per request.

## 4. Verify from outside the building

Run it from a network that shares none of your origin’s caches or hosts-file entries.

```bash
#!/usr/bin/env bash
# check-removed.sh removed-urls.txt   Read-only (GET). Lines: <old-url> <404|410|noindex|successor-url>
set -u
list="${1:-removed-urls.txt}"
[ -r "$list" ] || { echo "Cannot read $list" >&2; exit 2; }
fail=0
printf 'verdict\tfirst\tfinal\thops\tfinal_url\told_url\n'
while read -r url expected _ || [ -n "${url:-}" ]; do
  url=${url%$'\r'}; expected=${expected%$'\r'}
  case "$url" in ''|'#'*) continue ;; esac
  first=$(curl -q -sS -m 20 -o /dev/null -w '%{http_code}' "$url")
  read -r final hops final_url <<< "$(curl -q -sS -L --max-redirs 10 -m 20 -o /dev/null \
    -w '%{http_code} %{num_redirects} %{url_effective}' "$url")"
  verdict=FAIL
  case "$expected" in
    404|410) [ "$first" = "$expected" ] && verdict=PASS ;;
    noindex) [ "$final" = 200 ] && curl -q -sS -L -m 20 -D - "$url" 2>/dev/null |
               grep -Eiq '^x-robots-tag:.*noindex|<meta[^>]+noindex' && verdict=PASS ;;
    http*)   case "$first" in 301|308) [ "$final" = 200 ] && [ "$hops" = 1 ] && [ "$final_url" = "$expected" ] && verdict=PASS ;; esac ;;
    *)       verdict=CHECK ;;
  esac
  [ "$verdict" = PASS ] || fail=1
  printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$verdict" "$first" "$final" "$hops" "$final_url" "$url"
done < "$list"
exit "$fail"
```

```text
https://example.com/meetup-2024/ 410
https://example.com/guides/setup-v1/ https://example.com/guides/setup/
```

`first` is the status before redirects; chains (`hops` above 1) and CHECK rows fail; `noindex` is a text match. A PASS proves what your edge serves, not what a person is shown: close each URL with the query that used to surface it.

| Old URL | End state | Own surfaces cleared | Notified | Outside check | Recheck (date, engine, query) |
|---|---|---|---|---|---|
|  |  |  |  | PASS / FAIL | old claim gone / still served |

## Worked example

Two URLs leave a docs site on 2026-10-02: `/meetup-2024/` (no successor) and `/guides/setup-v1/` (replaced by `/guides/setup/`).

| Old URL | End state | Own surfaces cleared | Notified | Outside check | Recheck (date, engine, query) |
|---|---|---|---|---|---|
| /meetup-2024/ | 410 | sitemap, feed, `llms.txt`, 3 links | Removals; IndexNow | PASS 410, 0 hops | 2026-10-06, “meetup 2024”: gone in Google, old agenda in Bing |
| /guides/setup-v1/ | 301 to /guides/setup/ | 7 links, sitemap, JSON-LD | IndexNow (old and new) | PASS 301, 1 hop | 2026-10-06, “setup guide v1”: new guide in both |

**Verdict:** `/guides/setup-v1/` is removed. `/meetup-2024/` is not, because Bing still serves the old claim: block it there, keep the 410, recheck on 2026-10-09.

## Stop if

- No page clearly replaces the old one, yet a redirect is wanted to keep traffic: use 404 or 410.
- The URL must stay reachable for people and you cannot send `noindex`.
- A rebuild puts the URL back into a sitemap, feed, search data or `llms.txt`: fix the generator input.
- The outside check disagrees with your origin (the edge still serves 200): find out why first.
- You lack access to the search tools, or the removal is a legal, privacy or safety matter: hand it over.

## Sources

- Google Search Console Help: Removals and SafeSearch reports tool — https://support.google.com/webmasters/answer/9689846 (checked 2026-10-02)
- Google Search Central: How HTTP status codes affect Google’s crawlers — https://developers.google.com/crawling/docs/troubleshooting/http-status-codes (checked 2026-10-02)
- Google Search Central: Block Search indexing with noindex — https://developers.google.com/search/docs/crawling-indexing/block-indexing (checked 2026-10-02)
- IndexNow: FAQ — https://www.indexnow.org/faq (checked 2026-10-02)
- Bing Webmaster Tools: How to remove content from Bing and Copilot — https://www.bing.com/webmasters/help/content-removal-cb6c294d (checked 2026-10-02)
