DeadLinkPatrol › Blog

Broken Link Alerts in Slack: How to Set Up a Checker That Posts to Your Channel

· 7 min read

Email alerts about broken links tend to get lost. They go to one person, they land between invoices and newsletters, and when that person is on holiday nobody sees them. Posting alerts to a Slack channel fixes most of that. The whole team sees the alert, someone claims it with an emoji, and the thread becomes a record of the fix.

This guide covers what a useful broken-link alert looks like, the three ways to get alerts into Slack (with the limits of each), and how to stop the channel turning into noise that everyone mutes.

Before wiring anything up, decide what each message has to say. An alert that reads only "3 broken links found" makes someone log in to a dashboard, and most of the time they won't. A useful alert answers four questions in the message itself:

  1. Which site? This matters if you look after more than one.
  2. Which URL is broken, and how? A 404 (page gone) needs a redirect or a link edit. A 5xx (server error) might fix itself or might mean a dead plugin. "Unreachable" often means a DNS problem or an expired domain on an external site.
  3. Where is the link? You need the page the broken link sits on, because that page is what you edit.
  4. Is it new? An alert should only fire for breakage that wasn't there at the last check. If you get the full list every day, people stop reading by day three.

Keep those four in mind as you choose a setup, because the main difference between the options below is how much of this you get without extra work.

Option 1: A checker with a built-in Slack integration

Some link-monitoring tools connect to Slack directly. You authorize the app, pick a channel, and you're done. This is the least work if your tool offers it.

Things to check before you rely on it:

If your checker has no native Slack app but does offer webhooks, read on.

Option 2: Generic webhook → small relay → Slack incoming webhook

Many monitoring tools, DeadLinkPatrol included, send a generic JSON webhook when something breaks. You can't paste that webhook straight into Slack. Slack's incoming webhooks expect a specific payload with a text field (or Block Kit blocks), and Slack's documentation lists no_text as the error you get when text is missing. A tool's own payload format won't match.

The fix is a small relay: a function that receives the tool's webhook, checks that it's genuine, reformats it, and forwards it to Slack. It's about 40 lines of code and can run on a free serverless tier.

Step 1: Create the Slack incoming webhook

In Slack's app settings, create an app (or use an existing one), turn on Incoming Webhooks, click Add New Webhook to Workspace, and pick the channel. You'll get a URL like https://hooks.slack.com/services/T000/B000/XXXX. Treat it as a secret. Slack's docs say it actively looks for leaked webhook URLs and revokes them.

Step 2: Deploy the relay

Here's a relay written as a Cloudflare Worker. It expects the DeadLinkPatrol payload format, where the links.broken event carries site, newBroken (each item has url, status, kind and foundOn), totalBroken and reportUrl. It also checks the X-DeadLinkPatrol-Signature header, which looks like t=<unix time>,v1=<hex HMAC-SHA256 of "t.body">. If you use a different tool, change the field names and the signature check to match its docs.

// Cloudflare Worker: DeadLinkPatrol webhook → Slack incoming webhook
// Secrets: DLP_SECRET (your webhook secret), SLACK_URL (incoming webhook URL)
const enc = new TextEncoder();

async function hmacHex(secret, msg) {
  const key = await crypto.subtle.importKey(
    'raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(msg));
  return [...new Uint8Array(sig)].map(b => b.toString(16).padStart(2, '0')).join('');
}

export default {
  async fetch(req, env) {
    if (req.method !== 'POST') return new Response('ok');
    const raw = await req.text();
    const header = req.headers.get('x-deadlinkpatrol-signature') || '';
    const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
    if (!parts.t || Math.abs(Date.now() / 1000 - Number(parts.t)) > 300)
      return new Response('stale or missing timestamp', { status: 400 });
    if (await hmacHex(env.DLP_SECRET, parts.t + '.' + raw) !== parts.v1)
      return new Response('bad signature', { status: 401 });

    const p = JSON.parse(raw);
    let text;
    if (p.event === 'links.broken') {
      const lines = p.newBroken.slice(0, 10).map(b =>
        `• ${b.status || 'ERR'} ${b.url}\n   on ${b.foundOn?.[0] || 'unknown page'}`);
      const more = p.newBroken.length > 10 ? `\n…and ${p.newBroken.length - 10} more` : '';
      text = `:warning: ${p.newBroken.length} new broken link(s) on ${p.site} ` +
             `(${p.totalBroken} total)\n${lines.join('\n')}${more}\n<${p.reportUrl}|Open report>`;
    } else {
      text = `DeadLinkPatrol: ${p.message || p.event}`;
    }
    const r = await fetch(env.SLACK_URL, {
      method: 'POST', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ text }) });
    return new Response(r.ok ? 'sent' : 'slack error ' + r.status, { status: r.ok ? 200 : 502 });
  }
};

A few notes on the design:

Step 3: Point your checker at the relay

Paste the Worker's URL into your link checker's webhook setting and send a test event. In DeadLinkPatrol, webhooks are on paid plans, and there's a test button that sends {"event":"test", ...}. The relay above handles that event, so you should see a message in Slack within seconds.

Option 3: Slack Workflow Builder webhook trigger

Slack also lets a workflow start from a webhook. Workflow Builder gives you a URL under hooks.slack.com/triggers/..., you define variables, and any request whose JSON keys match those variables starts the workflow. The workflow can then post a formatted message, open a ticket in a connected app, or tag a person.

The trade-offs:

Workflow Builder works well for "send the team a heads-up with a link to the report." If you want the broken URLs in the message itself, use the relay.

Option 4: Zapier, Make, or n8n

No-code automation tools sit in the same place as the relay. They catch the webhook, let you pick fields, and post to Slack. They're quick to set up, but:

If you already pay for one of these tools for other work, it's a reasonable choice. If not, the relay is free and you control it.

Keeping the channel quiet enough that people read it

A broken-link channel only works if people trust that every message needs action. Some rules that help:

Alert only on new breakage. This is the most important rule. Your checker should compare each run with the previous one and only notify about the difference. If yours can't, put a small store (Workers KV, a JSON file, a sheet) in the relay and diff the lists yourself.

Re-verify before alerting. External sites rate-limit, time out, and go down for maintenance. Check a failure twice, a few minutes apart, before calling it broken.

Treat 403, 429 and 999 as "verify by hand", not "broken". Social networks and some large sites answer automated requests with these codes while working fine in a browser. Keep them out of the alert channel, or label them clearly.

Separate internal from external. A broken internal link is always yours to fix. A broken external link might mean editing your page, or it might mean the other site is down for an hour. Some teams send external-link alerts to a weekly digest instead of the live channel.

Thread the fix. When someone fixes a link, they reply in the thread with what they did ("301 from /old-pricing to /pricing"). Six months later, that thread tells you why the redirect exists.

Per-client channels for agencies

If you look after client sites, alert routing is where Slack really helps:

DeadLinkPatrol's Agency plan ($79/mo) covers 50 sites with daily scans and white-label client reports. If you want branded monthly health reports that also cover SSL, uptime and SEO basics, AgencyHealth starts at $49/mo for 50 sites. On either one, webhooks carry the same signed payload the relay above expects. Compare plans.

Quick checklist

Want to see what an alert would contain before setting any of this up? Run a free scan of up to 25 pages. No signup needed, and you'll see the exact broken URLs and the pages they're on.

Check your own site for broken links

Run a free scan of up to 25 pages, no signup. Or let DeadLinkPatrol watch your sites on a schedule and email you when a link breaks.

Monitoring: Starter $9/mo (3 sites, weekly) · Agency $79/mo (50 sites, daily, white-label reports). See plans

More from the blog

More free tools from Swift Labs