# Bulk URL checker: 1,000 status codes and redirects at once

> Paste an old sitemap or a redirect map into a free bulk URL checker and see the final status, hops, noindex and canonical of every URL, then fix a migration in the right order.

Updated 2026-09-25 · Technical SEO · HTML version: https://getreport.app/guides/bulk-url-checker-for-migrations

A migration is finished when every old address lands somewhere sensible, not when the new site is live. Nobody clicks through 800 old URLs by hand, so the check gets skipped and the traffic loss shows up in Search Console six weeks later. This guide turns any list of URLs into a table of answers in a few minutes, then shows how to read it and fix each kind of row.

## Quick answer

- Collect the old URLs (sitemap, Search Console export, redirect map, backlink report) and paste them into the [bulk URL checker](https://getreport.app/tools/bulk-url-checker), up to 1,000 per run.
- Filter to **Needs a look**: every row that is not a plain OK, labelled chain, temporary redirect, 4xx, 5xx, noindex or unreachable.
- **4xx on a page that had traffic or backlinks**: add a 301 to the closest equivalent page, not the home page.
- **Chain**: point the first hop straight at the final URL. **Temporary** (302/307) for a permanent move: change it to 301 or 308.
- **noindex**, or a **canonical** pointing at the old domain: leftovers from staging or the old CMS. Remove them.
- Download the CSV, fix, re-run the same list. The tiles should read 0 chains, 0 4xx, 0 noindex.

## Why bulk URL checks matter

Google's guidance on [site moves with URL changes](https://developers.google.com/search/docs/crawling-indexing/site-move-with-url-changes) comes down to one rule: every old URL redirects, once, with a permanent status, to the page that replaces it, for at least a year. In practice a migration leaks in five ways, each invisible in a browser and obvious in a table:

- **Chains.** Old page → old https address → new domain → new slug. Each hop costs a round trip and a little ranking signal; Google stops following after 10.
- **302 where 301 was meant.** Many hosts, CDNs and plugins default to a temporary redirect, which Google reads as "the old URL is still the real one".
- **Redirects to the home page.** Google treats a redirect to an unrelated page as a soft 404, so the page loses its rankings as if it had been deleted.
- **404s on pages with backlinks.** A discontinued product, a renamed category, a post nobody migrated. Every link pointing at it now passes nothing.
- **noindex and canonicals left over from staging.** Staging was noindexed so Google would not find it, and the canonicals were generated for the old domain. Both survived the switch. These are the silent killers: the page answers 200, looks fine, and drops out of the index.

### When you need this

Any change that touches addresses: a domain change, http to https, a new URL structure, a redesign, a CMS switch, a content clean-up. It is also the fastest audit of an old sitemap you inherited, a Search Console export full of "Not found" rows, or a redirect map someone else wrote.

### Where the list comes from

- **The old XML sitemap.** Fetch it before the switch and keep a copy; see [XML sitemap validation](https://getreport.app/guides/xml-sitemap-validation) for reading one.
- **Search Console.** Pages → export, or Performance → Pages → export for the last 16 months: the URLs Google actually sent people to.
- **Server logs** (every path Googlebot requested last month) and **backlink tools** (the "top pages by referring domains" report).
- **The redirect map itself.** Paste the source column and compare the final URLs with the target column (step 3).

## How getReport checks it

> **Free tool:** [Bulk URL checker: status codes and redirects for up to 1,000 URLs](https://getreport.app/tools/bulk-url-checker): Paste up to 1,000 URLs and get the HTTP status, the final URL after redirects, the number of hops, the time to first byte, the content type and the noindex and canonical signals of every one, in one table you can filter and download as CSV. Free, no account, no cap at 500.

Paste the list, one URL per line. Commas and tabs also separate, lines starting with `#` are ignored, duplicates are dropped, and a bare `example.com/page` is normalised to `https://`. Lines beyond 1,000 are reported as dropped for a second run. The form works without JavaScript.

Every URL is fetched with a `GET`, following up to 10 redirects, through the same SSRF guard as a report. The runner keeps 12 fetches in flight but at most 4 per host, so a 1,000-row list against one server takes a few minutes without overloading it. Each URL gets 20 s for its whole chain, and only the first 256 KB of the body are read, enough for the `<head>` of any page. Private addresses are skipped and labelled "Not checked". The run's link, `/tools/bulk-url-checker?run=…`, works while the run is still filling and for 30 days after.

![Bulk URL checker results: summary tiles and filter chips above one row per URL, including a three-hop "Redirect chain" row marked "mixed 301/302", a 404 marked "Not found" and a 200 page with a noindex chip](https://getreport.app/guides/img/bulk-url-checker-for-migrations/results-table.webp "One row per URL: final status, label, final address (chain behind a disclosure), hops, TTFB, type and the title, noindex and canonical signals.")

The redirect type is `permanent` when every hop is 301 or 308, `temporary` when every hop is 302, 303 or 307, `mixed` otherwise. noindex is read from `meta robots`, `meta googlebot` and the `X-Robots-Tag` header; the canonical is resolved to an absolute URL. The same signals are findings in a full report:

> **Check: The URL loads without a redirect chain.** Each redirect is a full round trip before the browser can start loading, often 100–300 ms on mobile. Search engines pass less value with every hop and stop following after a few.
>
> 1. Point every old URL straight at the final one (a single 301), and update internal links to use the final URL directly.
> 2. Look for rules that stack, for example http → https, then non-www → www, then trailing slash; combine them into one rule.

> **Check: No redirects before the page.** Every redirect is a full round trip before the browser can even ask for the page. http → https → www → trailing slash can easily add a second on mobile.
>
> 1. Link straight to the final URL everywhere (menus, ads, social profiles).
> 2. Collapse chains: redirect http and non-www directly to the final https URL in one hop.

> **Check: The page is not set to noindex.** A noindex directive tells Google to drop this page from search results. Unless that is intended, every visit from search is lost.
>
> 1. Remove noindex from the robots meta tag and from the X-Robots-Tag header, unless the page should stay out of search.
> 2. In WordPress, untick Settings → Reading → "Discourage search engines" and check the page's SEO plugin settings.

> **Check: Canonical tag is present.** Without a canonical URL, search engines may index duplicate versions of this page (with and without trailing slash, with UTM parameters) and split its ranking signals.
>
> 1. Add <link rel="canonical" href="https://your-site.com/page/"> in <head>.
> 2. In WordPress, Yoast or Rank Math add this automatically — check it is not disabled for this page.

Each row gets one label, in the order a migration cares about:

| Label | Meaning |
| --- | --- |
| OK | 2xx, no redirect, no noindex |
| Redirect | One hop to a final answer |
| Redirect chain | Two or more hops |
| Not found | Final status 4xx |
| Server error | Final status 5xx |
| noindex | 200 directly, but the page asks not to be indexed |
| Unreachable | DNS failed, timed out, connection refused, or more than 10 redirects |
| Not checked / Not a URL | A private address the guard skipped, or a line that is not an address |

The tiles count OK, Redirects, Chains, 4xx, 5xx and noindex; the filter chips (All, Needs a look, Redirects, 4xx / 5xx, noindex, Unreachable) are part of the link, so a colleague can open the filtered view. TTFB turns amber over 800 ms and red over 1,800 ms. Limits: 6 runs per hour and 5,000 URLs per day per visitor, no account.

### Status against final status

The status column is always the final one. A `200` with 2 hops is a working page reached badly; a `404` with 1 hop is a redirect pointing at nothing, worse than a plain 404 because the rule looks correct to whoever wrote it.

One 301 is fine; two is not: each hop is a round trip for visitors and a small loss of signal for search. [Redirects without chains](https://getreport.app/guides/redirects-without-chains) covers the status codes; for this table the target is `permanent` and 1 hop.

> **Watch out:**
> "Redirects to the home page" is not a label the tool can give, because it does not know which page is your home page. Sort the CSV by `final_url`: a block of rows all ending at `/` is the pattern. Google's [soft 404 guidance](https://developers.google.com/search/docs/crawling-indexing/http-network-errors#soft-404-errors) counts a redirect to an unrelated page as a soft 404; treat those rows as real 404s.

## Step by step

### 1. Assemble the list

Merge the sources into one spreadsheet column. Keep the old domain and scheme exactly as they were (`http://old.example.com/page`), because that is what Google and the backlinks still request; a list of new addresses only proves the new site works. Remove nothing; duplicates are dropped.

### 2. Run it

Paste the column into the [bulk URL checker](https://getreport.app/tools/bulk-url-checker), start the run and copy its link. A 1,000-row list against one healthy server finishes in two to four minutes.

### 3. Filter to "Needs a look"

Everything that is not a plain OK, worst first: 5xx, 4xx, chains, temporary redirects, noindex, canonicals.

![The same run filtered to 4xx / 5xx: the filter chip is active and the table lists only the row whose final status is 404](https://getreport.app/guides/img/bulk-url-checker-for-migrations/filtered.webp "The 4xx / 5xx filter is part of the run link, so the list of pages to redirect can be shared as it is.")

If you ran the source column of a redirect map, check the targets now. Download the CSV and put the map on a second sheet (source in column A, intended target in column B). In the CSV, `url` is column B and `final_url` column D (the header row names all 15 columns, from `input` to `error`):

```text
=IF(VLOOKUP(B2, map!A:B, 2, FALSE)=D2, "ok", "wrong target")
```

Fill it down and filter for "wrong target". Trailing slashes count: `/new-page` and `/new-page/` are different strings, and usually the mismatch.

### 4. Fix 4xx and 5xx

A 5xx is the new server failing on that request; if it still fails in a browser, it is a bug on the page, not a redirect problem. A 4xx is a page the migration lost. If it had traffic, backlinks or a replacement, redirect it to the closest equivalent: the new version of the page, its category, or the most similar product. If it had none, a clean 404 or 410 is right.

On Apache, in `.htaccess` at the site root:

```apache
Redirect 301 /old-page/ https://www.example.com/new-page/
Redirect 301 /products/blue-widget https://www.example.com/shop/widgets/blue/
```

On nginx, in the `server` block, one exact-match location per page:

```nginx
location = /old-page/ {
    return 301 https://www.example.com/new-page/;
}
```

Redirect to the absolute final URL, with the scheme, host and slash the new site actually uses, so the rule lands in one hop. For hundreds of rules, nginx's `map` directive keyed on `$request_uri` keeps them in one file; on WordPress the Redirection plugin imports the map as a CSV (Platform notes).

### 5. Collapse chains

Open the chain disclosure on each Redirect chain row. The first hop is the rule you own; the hops after it are other layers (http → https at the CDN, non-www → www at the server, an old redirect that now points at a moved page). Change the first hop's target to the final URL. A chain like `old → old-https → new → new-slash` means the map was written with the old scheme or without the trailing slash; fix the map, not the layers.

### 6. Turn temporary into permanent where the move is final

Rows with `temporary` or `mixed 301/302` under the label are the ones Google keeps indexing at the old address. If the page is not coming back, make it 301 (or 308 for anything that receives form posts): `return 301` on nginx, `Redirect 301` rather than a bare `Redirect` on Apache (which sends 302), the status field of a Cloudflare rule.

### 7. Remove stray noindex and fix canonicals

The noindex filter lists pages that answer 200 directly and ask not to be indexed. After a migration that is nearly always a staging setting: "Discourage search engines" in WordPress, a hosting-panel rule, or an `X-Robots-Tag` header the staging server added and production inherited. Remove it at the source; the [noindex reference](https://getreport.app/guides/noindex-nofollow-complete-reference) lists every place it can hide. A redirect that ends on a noindex page keeps its Redirect label but still shows the noindex chip in the last column.

Canonicals appear in the last column only when they differ from the final URL, which is the case that matters here. A canonical still pointing at `old.example.com` tells Google the old page is the real one, so the new page never replaces it. The fix is the CMS's site URL setting or the SEO plugin, not the template: see [Canonical tags explained](https://getreport.app/guides/canonical-tags-explained).

### 8. Export, fix, re-run, compare

Download the CSV before you change anything; it is the "before". Apply the fixes, paste the same list into a new run and keep both links. The tiles should now read 0 chains, 0 4xx, 0 5xx and 0 noindex; a `VLOOKUP` between the two CSVs on `url` shows every row that changed.

## Platform notes

### WordPress

The Redirection plugin (Tools → Redirection → Import/Export) imports a CSV with the columns `source,target` and creates 301s for all of them; use it rather than editing `.htaccess` on a managed host. A permalink change (Settings → Permalinks) makes WordPress redirect old `?p=123` addresses itself, but not old slugs, so those go in the map. Untick Settings → Reading → "Discourage search engines" on production, and after a domain move run `wp search-replace` so internal links and canonicals stop pointing at the old host.

### Shopify

Redirects live under Online Store → Navigation → URL redirects; the Import button takes a CSV with the columns `Redirect from` and `Redirect to`. Shopify serves them as 301 and adds one when you change a product or collection handle, but not for deleted products, and old paths that land on `/` count as soft 404s.

### Static sites and custom stacks

Rules go in the host's config: `_redirects` on Netlify (`/old-page/ /new-page/ 301`), `redirects` in `vercel.json` with `"permanent": true`, a Cloudflare Bulk Redirects list for hundreds of rules, or the nginx and Apache forms above. Watch for two layers doing half a job each (the CDN redirects http → https, the origin redirects the path): put the whole rule at the first layer that answers.

## Verify

- Re-run the same list. Needs a look is empty apart from rows you chose to leave (deliberate 404s, one-hop permanent redirects).
- Spot-check the most important old URLs with the [redirect checker](https://getreport.app/tools/redirect-checker), which shows one URL's full chain plus the http and www variants the bulk tool did not ask for.
- In Search Console, Pages → "Page with redirect" grows and "Not found (404)" shrinks over the following weeks. Google recrawls a moved site over weeks to months; judge it at four weeks, not four days.
- Run a full report on one new page: the redirect findings pass, and the [indexability learn page](https://getreport.app/learn/indexability) explains anything left.

## Common mistakes

- **Bulk 302 from a plugin or CDN default.** Symptom: `temporary` under every Redirect label; the old URLs stay indexed. Change the default to 301 and re-run.
- **Redirecting everything to the home page.** Symptom: hundreds of rows with `/` as the final URL, which Google counts as soft 404s. Map each page to its equivalent or let the 404 stand.
- **Forgetting the http and www variants.** Only `https://www.` was mapped, but half the backlinks say `http://` or the bare host. Symptom: chains starting at the old host, or Unreachable rows when it stopped answering http. Add the variants to the list and give each a one-hop rule.
- **Leaving the staging noindex on.** Symptom: the noindex tile is not 0 on a site that should be fully indexed. Untick the setting and remove the header on production.
- **Redirect loops.** A rule sends `/new-page` to `/new-page/` while the CMS sends it back. Symptom: Unreachable rows with "More than 10 redirects". Pick one slash policy and make every layer agree; the [redirects learn page](https://getreport.app/learn/redirects) has the short version.
