AutomationFlowsSlack & Telegram › Monitor Landing Page Defacement with Snapshot Site, Discord and Email

Monitor Landing Page Defacement with Snapshot Site, Discord and Email

BySnapshot Site @snapshotsite on n8n.io

This workflow runs every 15 minutes to compare a live landing page against a baseline screenshot using Snapshot Site, and alerts your team via Discord and email when the mismatch indicates possible defacement or hijacking. Runs every 15 minutes on a schedule. Uses Snapshot Site…

Cron / scheduled trigger★★★★☆ complexity12 nodesN8N Nodes Snapshot SiteDiscordEmail Send
Slack & Telegram Trigger: Cron / scheduled Nodes: 12 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #17945 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "00JvXPQ97EJUyIlv",
  "name": "Landing Page Defacement / Hack Monitor",
  "tags": [],
  "nodes": [
    {
      "id": "ba27497e-e526-4868-9387-b81c2ee04afc",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1664,
        64
      ],
      "parameters": {
        "width": 480,
        "height": 832,
        "content": "## Landing Page Defacement / Hack Monitor\n\n### How it works\n\nThis workflow runs every 15 minutes and compares the live landing page against a stored known-good baseline image. It checks for API or comparison errors, then treats a mismatch of 30% or more as a possible defacement or hijack. Alerts go to Discord and email in parallel so the incident can't be missed, and comparison failures are reported to Discord so the monitor never breaks silently.\n\n### Setup steps\n\n- Install the `n8n-nodes-snapshot-site` community node (Settings \u2192 Community nodes \u2192 Install \u2192 `n8n-nodes-snapshot-site`) \u2014 verified by n8n.\n- Add a Snapshot Site credential; set `beforeUrl` to a stored baseline screenshot URL and `afterUrl` to the live page \u2014 full URLs including `https://` (never set both `beforeUrl` and `beforeOptions.imageUrl` \u2014 the API rejects that combination).\n- Add a Discord Webhook credential and an SMTP credential; set the target channel and recipients.\n- Tune the schedule interval and the 30% alert percentage to your risk tolerance.\n\n### Customization\n\nLower the alert percentage to over-alert rather than risk missing an incident, add more parallel alert channels (SMS, PagerDuty) off the true branch, and refresh the baseline image after every intentional design change to avoid false alarms."
      },
      "typeVersion": 1
    },
    {
      "id": "ba70c21e-1bf7-469f-8793-ec7298aaf7db",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1120,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 352,
        "content": "## Schedule and compare\n\nStarts the workflow every 15 minutes and compares the live page against the stored baseline image."
      },
      "typeVersion": 1
    },
    {
      "id": "63423018-86d2-40d9-ac00-ee948e4fec78",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 544,
        "content": "## Handle compare errors\n\nChecks whether the comparison returned an API or comparison error and sends a Discord failure notification on the lower error branch."
      },
      "typeVersion": 1
    },
    {
      "id": "0d74b19a-d1f4-40a3-a6c8-ace280c3aac6",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 656,
        "content": "## Report defacement result\n\nEvaluates successful comparison results; on an abnormal change it alerts Discord AND email in parallel, otherwise it ends quietly."
      },
      "typeVersion": 1
    },
    {
      "id": "9b2edd7c-9718-45db-bcac-2c3a0f4522f0",
      "name": "When Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1072,
        432
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c2f87c9b-9cbb-44ca-a12a-da062ded9ef9",
      "name": "Compare Baseline vs Live",
      "type": "n8n-nodes-snapshot-site.snapshotSite",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        -752,
        432
      ],
      "parameters": {
        "afterUrl": "https://example.com",
        "beforeUrl": "https://your-storage.example.com/baselines/homepage-baseline.png",
        "operation": "compare",
        "threshold": 0.3,
        "afterOptions": {},
        "beforeOptions": {},
        "requestOptions": {}
      },
      "credentials": {
        "snapshotSiteApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 5000
    },
    {
      "id": "a4e522e4-aba8-4c6c-8673-1be94d43366e",
      "name": "Check for API Error",
      "type": "n8n-nodes-base.if",
      "position": [
        -464,
        432
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.error === true }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2c8167c5-4b26-462d-a8c7-82ee98adf9e2",
      "name": "Check for Abnormal Change",
      "type": "n8n-nodes-base.if",
      "position": [
        48,
        352
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json.summary.mismatchPercentage}}",
              "value2": 30,
              "operation": "largerEqual"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7be5e10c-9029-4e31-98ff-18132b8e89a6",
      "name": "Notify Defacement on Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        320,
        144
      ],
      "parameters": {
        "content": "=\ud83d\udea8 **Possible defacement/hack detected** on {{ $json.after.source }} \u2014 mismatch **{{ $json.summary.mismatchPercentage }}%**\nDiff: {{ $json.diff.link }}\nInvestigate immediately.",
        "options": {},
        "authentication": "webhook"
      },
      "credentials": {
        "discordWebhookApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8534fce8-149a-47d9-8f00-4f747a48da52",
      "name": "Notify Defacement by Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        320,
        336
      ],
      "parameters": {
        "text": "=Mismatch: {{ $json.summary.mismatchPercentage }}%\nDiff image: {{ $json.diff.link }}\nLive page: {{ $json.after.source }}\nBaseline image: {{ $json.before.link }}",
        "options": {},
        "subject": "\ud83d\udea8 Possible defacement detected",
        "toEmail": "user@example.com",
        "fromEmail": "user@example.com",
        "emailFormat": "text"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "5fa84c05-7d4d-4fd4-9fd7-7b221a2128e1",
      "name": "No Change",
      "type": "n8n-nodes-base.noOp",
      "position": [
        320,
        512
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "9d5317de-9df0-4b18-8b1e-602c1b9e0fb0",
      "name": "Notify Comparison Failure on Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        -192,
        624
      ],
      "parameters": {
        "content": "=\ud83d\udd34 Compare call failed: {{ $json.error?.message || $json.message }}",
        "options": {},
        "authentication": "webhook"
      },
      "credentials": {
        "discordWebhookApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "a442ce74-fd42-4b25-8f82-4a860831cdf9",
  "nodeGroups": [],
  "connections": {
    "Check for API Error": {
      "main": [
        [
          {
            "node": "Notify Comparison Failure on Discord",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check for Abnormal Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Compare Baseline vs Live",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Baseline vs Live": {
      "main": [
        [
          {
            "node": "Check for API Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Comparison Failure on Discord",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for Abnormal Change": {
      "main": [
        [
          {
            "node": "Notify Defacement on Discord",
            "type": "main",
            "index": 0
          },
          {
            "node": "Notify Defacement by Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs every 15 minutes to compare a live landing page against a baseline screenshot using Snapshot Site, and alerts your team via Discord and email when the mismatch indicates possible defacement or hijacking. Runs every 15 minutes on a schedule. Uses Snapshot Site…

Source: https://n8n.io/workflows/17945/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

🌸 Affirmation Sender + Weekly Gratitude Digest v2

Email Send, Telegram, Notion +3
Slack & Telegram

debug. Uses httpRequest, slack, redis, mailgun. Scheduled trigger; 60 nodes.

HTTP Request, Slack, Redis +2
Slack & Telegram

Wait Schedule. Uses spotify, supabase, compareDatasets, noOp. Scheduled trigger; 54 nodes.

Spotify, Supabase, YouTube +1
Slack & Telegram

N8N. Uses github, discord, postgres. Scheduled trigger; 38 nodes.

GitHub, Discord, Postgres
Slack & Telegram

This n8n template demonstrates how to automatically monitor and track username and nickname changes across your Discord server members. Perfect for community moderation, security monitoring, and maint

Discord, Data Table