# Website Migration Checker – Find Broken Redirects & 404s (`mauberme/site-migration-auditor`) Actor

Moving a website to a new domain or new URLs? Paste your list of old and new addresses: this tool opens every old link and checks it lands on the right new page. Get a 0-100 report of broken links and errors, or ready-made redirect rules for your hosting. Free during launch.

- **URL**: https://apify.com/mauberme/site-migration-auditor.md
- **Developed by:** [Mauberme](https://apify.com/mauberme) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Website Migration Checker – Find Broken Redirects & 404s

**What is it for?** Changing your website's domain or URL structure is the fastest way to lose Google traffic: every old link that does not send visitors to the right new page is lost. Give this tool your list of **old address → new address** and it opens every old link for you, tells you which ones are broken, and gives you a **score from 0 to 100** you can show a client. It can also **write the redirect rules** for your hosting so you don't have to.

It can also **generate the redirect rules for you**, ready to paste into **Next.js, Vercel, Netlify, nginx, Apache (.htaccess) or Cloudflare Bulk Redirects**, fixing chains, duplicates and rule order on the way.

Built from the tooling used to migrate a real multilingual site (3 legacy domains, 89 redirects, 4 languages).

### Who is it for

- **Agencies** auditing a client migration before and after launch.
- **Developers** who need a correct redirects file for their platform, not a hand-written one.
- **Multilingual sites** checking that hreflang alternates are valid and reciprocal.

### What it checks (audit mode)

| Check | What it catches |
|---|---|
| Redirects | Old URL does not redirect, wrong destination, redirect loops, chains (A→B→C), temporary 302/307 instead of 301/308, too many hops, leftover query strings |
| Destinations | The final page answers 404/5xx |
| Hreflang (from the sitemap) | Non-reciprocal alternates, missing self-reference, missing x-default, invalid or duplicated language codes |

Every redirect is followed **one hop at a time**, so you see the full chain and the status code of each hop.

### Input examples

**Audit a migration**

```json
{
  "mode": "audit",
  "redirectMap": "oldUrl,newUrl,status\nhttps://old-site.com/about,https://new-site.com/en/about,301\nhttps://old-site.com/blog?id=12,https://new-site.com/en/blog/my-post,301",
  "sitemapUrl": "https://new-site.com/sitemap.xml",
  "maxUrls": 500,
  "concurrency": 5
}
```

**Build redirect rules**

```json
{
  "mode": "build-rules",
  "platform": "nginx",
  "redirectMap": "https://example.com/my-redirect-map.csv"
}
```

The redirect map can be **CSV text, the URL of a CSV file, or an array of `{oldUrl, newUrl, status}` objects**. Column names are flexible (`from`/`to`, `source`/`destination`, `old_url`/`new_url`…); `status` defaults to 301. Use `oldSiteBaseUrl` / `newSiteBaseUrl` if your map uses paths instead of full URLs.

### Output

**Dataset**, one row per checked URL:

```json
{
  "type": "redirect",
  "oldUrl": "https://old-site.com/about",
  "expectedUrl": "https://new-site.com/en/about",
  "finalUrl": "https://new-site.com/en/about",
  "statusCodes": [301, 200],
  "hops": 1,
  "ok": true,
  "issues": [],
  "details": []
}
```

**Key-value store**

- `report.html` – a self-contained report: overall score, redirect score, hreflang score, issues by type and the list of failing URLs (worst first).
- `OUTPUT` – the same summary as JSON (score, counts per issue, report URL), handy for automations.
- In build-rules mode: the rules file (`next-redirects.mjs`, `vercel.json`, `_redirects`, `nginx-redirects.conf`, `redirects.htaccess` or `cloudflare-bulk-redirects.csv`) plus warnings about duplicates, conflicts, loops, flattened chains and reordered rules.

### How the score works

Each URL starts at 100 %. A broken redirect, a loop or an error page costs the whole URL; a wrong destination 80 %; a temporary 302/307 30 %; a chain 20 %; a leftover query string on the right page 10 %. Hreflang problems cost 10–40 % per page. The overall score is 70 % redirects and 30 % hreflang when both are checked.

### Pricing

**Free during launch.** This Actor has no usage fee of its own: you only pay Apify's standard platform usage for your runs (on the free Apify plan that is covered by your monthly credit). A pay-per-result price may be introduced later; Apify notifies users in advance of any price change.

### Limits and good manners

- Up to 5,000 URLs per run (`maxUrls`, default 500).
- Up to 20 parallel requests (default 5). A `429 Too Many Requests` pauses the whole run for that host and respects `Retry-After`.
- Identifiable User-Agent: `SiteMigrationAuditor`.
- Only public http(s) addresses are visited: private networks, localhost and cloud metadata addresses are blocked, including when a redirect points to them.
- Hreflang reciprocity is checked against the sitemap. If your sitemap lists only one language and declares the others as alternates, those alternates are reported as "not listed in the sitemap" (information only, not scored).

### FAQ

**Does it change anything on my site?** No. It only sends GET requests to the URLs in your map and sitemap.

**Can I run it before launch?** Yes: use build-rules to create the config, deploy it to staging, then audit the staging URLs.

**Why is a leftover query string only a small penalty?** Some platforms (Next.js, for example) pass the old query string to the destination. The page is right, but you may want to strip it to avoid duplicate URLs.

# Actor input Schema

## `mode` (type: `string`):

Audit visits your live site. Build rules turns your redirect map into a config file for your hosting platform.

## `redirectMap` (type: `string`):

CSV with columns oldUrl,newUrl and optional status (301 by default). You can also paste the URL of a CSV file.

## `sitemapUrl` (type: `string`):

Optional. The sitemap.xml (or sitemap index) of the new site. Used to check that hreflang alternates are reciprocal, self-referencing and valid.

## `platform` (type: `string`):

Where you will paste the generated rules.

## `newSiteBaseUrl` (type: `string`):

Optional. Lets you write destinations as paths (/about) instead of full URLs.

## `oldSiteBaseUrl` (type: `string`):

Optional (audit). Lets you write old URLs as paths. Needed to visit them.

## `checks` (type: `array`):

Which checks to run: redirects, hreflang. Leave empty for both.

## `maxUrls` (type: `integer`):

Hard cap for redirects and sitemap pages. Keeps the cost predictable.

## `concurrency` (type: `integer`):

Be gentle with the audited site. 429 responses slow the whole run down automatically.

## `maxRedirectHops` (type: `integer`):

Longer chains are reported as errors.

## `timeoutSeconds` (type: `integer`):

Per request.

## `ignoreTrailingSlash` (type: `boolean`):

When comparing the final URL with the expected one.

## `ignoreProtocol` (type: `boolean`):

When comparing the final URL with the expected one.

## `ignoreQueryString` (type: `boolean`):

When comparing the final URL with the expected one.

## Actor input object example

```json
{
  "mode": "audit",
  "redirectMap": "oldUrl,newUrl,status\nhttp://apify.com/,https://apify.com/,301\nhttps://apify.com/store/,https://apify.com/store,301",
  "platform": "next",
  "checks": [
    "redirects",
    "hreflang"
  ],
  "maxUrls": 500,
  "concurrency": 5,
  "maxRedirectHops": 10,
  "timeoutSeconds": 15,
  "ignoreTrailingSlash": true,
  "ignoreProtocol": false,
  "ignoreQueryString": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `report` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "redirectMap": `oldUrl,newUrl,status
http://apify.com/,https://apify.com/,301
https://apify.com/store/,https://apify.com/store,301`
};

// Run the Actor and wait for it to finish
const run = await client.actor("mauberme/site-migration-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "redirectMap": """oldUrl,newUrl,status
http://apify.com/,https://apify.com/,301
https://apify.com/store/,https://apify.com/store,301""" }

# Run the Actor and wait for it to finish
run = client.actor("mauberme/site-migration-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "redirectMap": "oldUrl,newUrl,status\\nhttp://apify.com/,https://apify.com/,301\\nhttps://apify.com/store/,https://apify.com/store,301"
}' |
apify call mauberme/site-migration-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mauberme/site-migration-auditor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/UNgKGdMKaclfUuZ9c/builds/pXy30ICWRquYUHdPb/openapi.json
