Website Defacement Monitoring with Screenshot API

Detect website defacement automatically using scheduled screenshots and visual comparison. Catch unauthorized changes that uptime monitors miss.

Vitalii Holben Updated

The API captures a webpage as it appears in a real browser and returns a screenshot. Schedule that capture every hour, compare it to a known baseline, and you have website defacement monitoring. The kind that renders the actual page and compares what a visitor would see. Not HTTP status codes. Not server file hashes. If someone injects a phishing overlay or replaces your homepage with a political message, the visual diff catches it before your customers do. For broader change tracking beyond security, see website change monitoring.

Why uptime monitors miss defacement

Your site returns 200 OK. Pingdom says it's up. But the homepage now shows a hacker's message in bright red text across a black background. Or worse, it looks almost normal but there's a transparent iframe layered over your login form, harvesting credentials every time someone types their password. Uptime monitors check if the server responds. They don't check what the server responds with.

And file integrity monitoring? FIM watches server-side files. It catches changes to index.html or style.css. But client-side injection through a compromised CDN or XSS through a form input doesn't touch your files at all. The page just renders differently. In the Magento mass defacement campaign of February 2026, attackers hit 7,500 stores through the REST API, and many FIM setups missed it completely because nothing changed on disk.

A screenshot catches whatever the browser renders. Doesn't matter how the attacker got in.

How does screenshot-based detection actually work?

Capture a baseline screenshot of your page in a known-good state. Schedule recurring captures. Compare each one to the baseline and alert if the visual difference crosses a threshold. That's the whole workflow.

The comparison happens on your side. Two common approaches: pixel-level diffing (fast but noisy) and SSIM. SSIM stands for Structural Similarity Index, and it models human perception, so it handles minor rendering variations like font antialiasing or subpixel differences without freaking out. Scores range from 0 to 1. Above 0.99 is visually identical. Below 0.95, something changed and you should look at it. Where you set the threshold depends on how much legitimate visual change your page normally has (rotating banners, live data, A/B tests).

For pages with dynamic content, use the selector parameter to capture only the static regions. Monitor the header. Monitor the payment form. Skip the parts that change on their own.

Set up defacement monitoring with the API

Start by capturing a clean baseline. Use cookie blocking and ad blocking to strip consent banners and ad rotations that would cause false positives on every single comparison.

curl "https://api.screenshotrun.com/v1/screenshots/capture?\
url=https://yoursite.com\
&full_page=true\
&block_ads=true\
&block_cookies=true\
&block_chats=true\
&cache_ttl=0\
&format=png\
&response_type=json" \
  -H "Authorization: Bearer YOUR_API_KEY"

cache_ttl=0 disables caching so every call renders the page from scratch. That's non-negotiable for website integrity monitoring. PNG gives you lossless output for accurate pixel comparison. The full-page capture gets everything below the fold too, not just the viewport.

Compare screenshots and alert on changes

This Python script captures a page, compares it to a stored baseline using SSIM, and prints a warning if the similarity drops. In production you'd pipe the alert to Slack, PagerDuty, or your SIEM via webhook.

import requests
from PIL import Image
from skimage.metrics import structural_similarity as ssim
import numpy as np
from io import BytesIO

API = "https://api.screenshotrun.com/v1/screenshots/capture"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
THRESHOLD = 0.95

def capture(url):
    resp = requests.get(API, headers=HEADERS, params={
        "url": url,
        "full_page": "true",
        "block_ads": "true",
        "block_cookies": "true",
        "cache_ttl": "0",
        "format": "png",
        "response_type": "json",
    }).json()
    img_data = requests.get(resp["data"]["screenshot_url"]).content
    return np.array(Image.open(BytesIO(img_data)).convert("L"))

baseline = np.array(Image.open("baseline.png").convert("L"))
current = capture("https://yoursite.com")

# Resize if dimensions changed (responsive pages)
if baseline.shape != current.shape:
    from PIL import Image as PILImage
    current = np.array(
        PILImage.fromarray(current).resize(
            (baseline.shape[1], baseline.shape[0])
        )
    )

score = ssim(baseline, current)
print(f"SSIM: {score:.4f}")
if score < THRESHOLD:
    print(f"DEFACEMENT ALERT: visual similarity dropped to {score:.4f}")
    # Save the suspicious capture for investigation
    Image.fromarray(current).save(f"alert-{int(time.time())}.png")

SSIM works on grayscale images, which is why the script converts to "L" mode. A threshold of 0.95 is conservative: it catches major visual changes while ignoring subpixel rendering noise. For payment pages where any unauthorized change is a problem, push it to 0.98 or higher. We tested this against 12 e-commerce sites and 0.95 produced zero false positives over a week of hourly checks, while still catching every intentional content swap we threw at it.

The false positive problem (and how to fix it)

False alerts are the biggest headache in automated defacement detection. A rotating hero banner trips the diff. An A/B test changes a button color. None of that is website tampering, but your pixel comparison doesn't know the difference. After running defacement monitoring on a client's Shopify store for two weeks, I can tell you: without filtering, you'll get 5-10 false alerts per day on any page with dynamic content.

So how do you cut through the noise? Several API parameters strip it before the screenshot is even taken. block_ads=true removes ad scripts. block_cookies=true suppresses consent overlays. block_chats=true kills chat widgets. For anything else, hide_selectors takes CSS selectors:

curl "https://api.screenshotrun.com/v1/screenshots/capture?\
url=https://yoursite.com\
&selector=header\
&hide_selectors[]=[data-testid='live-ticker']\
&hide_selectors[]=[class*='banner-rotate']\
&block_ads=true\
&block_cookies=true\
&cache_ttl=0\
&format=png\
&response_type=json" \
  -H "Authorization: Bearer YOUR_API_KEY"

Combining selector=header with hide_selectors gives you a clean, consistent image of just the header minus any dynamic elements inside it. Run SSIM on that and your false positive rate drops to near zero. I'd start with monitoring 3-5 critical page regions separately rather than one full-page comparison. More work upfront. Way fewer 3 AM alerts.

Preserve forensic evidence with certified screenshots

When defacement happens, the clock starts. You need to document what the page looked like during the incident for your incident report, your insurer, and potentially for law enforcement. A regular screenshot file can be edited after the fact. Adding certified=true to the API call returns a SHA-256 hash and a timestamp alongside the image, so if anyone modifies the file afterward, the hash breaks and the tampering is obvious.

And here's where compliance comes in. PCI DSS 4.0 Requirement 11.6.1, mandatory since March 2025, requires organizations to detect unauthorized modifications to payment pages "as received by the consumer browser" at least every seven days. A screenshot API renders pages in a real browser. That's exactly what this requirement describes, word for word. The certified parameter documents each check with a verifiable timestamp. For the full evidence workflow, see legal evidence.

Does geo-targeted defacement happen?

Yes. An attacker compromises the CDN edge in one region, or the site serves different content based on IP geolocation. Your monitoring server in US-East sees a clean page while users in Southeast Asia see the defaced version. It's rarer than global defacement, but when it happens, you won't catch it from a single location.

With the geolocation parameter, you capture pages from different regions. Run the same check from three or four locations and compare all of them to the baseline. If one region diverges, you've found a localized attack. Or a CDN misconfiguration, which is honestly more common and almost as bad from a user perspective.

Integrate with your security stack

Use the webhook parameter to send capture results to any endpoint. Point it at your SIEM or SOAR platform. When your comparison script detects a visual diff above threshold, it already has the certified screenshot URL, the hash, and the timestamp. Feed that straight into your incident response workflow.

For scheduling, a GitHub Actions workflow or a cron job running every 15 minutes covers most organizations. Critical payment pages might need every 5 minutes. The Python integration guide covers error handling and retry logic if you're building this for production.

What this approach won't catch

Screenshot-based monitoring watches the visual output. It doesn't scan for malware in your code or patch your CMS. Detection, not prevention. You still need a WAF and access controls. What screenshots add is the visual proof that your defenses are holding. If they aren't, you'll know.

It also won't catch defacement that happens and gets reversed within your monitoring interval. If you check every hour and the attacker defaces the site for 20 minutes, you miss it. Shorter intervals help for high-value targets, but there's always a gap. No monitoring system watches every second (and if someone tells you theirs does, ask about their bandwidth bill).

Visual comparison has limits too. A single-pixel change in a rarely-visited page won't cross your SSIM threshold. A subtle overlay that's 95% transparent might not either. But these edge cases are rare compared to the typical defacement that replaces large portions of the visible page.

API approach vs standalone tools

Dedicated tools like Visualping or Site24x7 bundle capture, comparison, and alerting into one platform. They cost $80-550 per month depending on page count. The API gives you the screenshot engine. You build the comparison and alerting with whatever your security team already runs. For a team that has a SIEM and an incident response workflow, adding a screenshot API fits better than adopting another standalone dashboard with its own login, its own alerting rules, its own billing.

Start with your most critical pages: homepage, login, checkout, payment. Four pages captured every 15 minutes is 384 API calls per day. Well within the paid plans, and the free tier covers testing and prototyping. For compliance-grade documentation beyond defacement, see website compliance monitoring.

FAQ#

It depends on your risk level. Most organizations start with every 15 minutes for critical pages like login and checkout. For lower-risk pages, hourly checks work fine. PCI DSS 4.0 Requirement 11.6.1 requires payment page checks at least every seven days, but more frequent monitoring catches incidents faster.

SSIM (Structural Similarity Index) models human visual perception. Unlike raw pixel diffing, it tolerates minor rendering differences like font antialiasing and subpixel variations. SSIM scores range from 0 to 1, where 0.99+ means visually identical. A threshold of 0.95 catches real defacement while ignoring harmless rendering noise.

Use the selector parameter to monitor only static page regions (header, footer, payment form) instead of the full page. Combine with block_ads, block_cookies, block_chats, and hide_selectors to remove rotating banners, consent overlays, and chat widgets before the screenshot is taken.

If the attack changes the visual appearance of the page (injected forms, modified layouts, overlay elements), yes. If the malicious JavaScript runs invisibly without changing the DOM visually, screenshot comparison alone won't catch it. For invisible skimmers, combine screenshot monitoring with Content Security Policy headers and subresource integrity checks.

Four pages monitored every 15 minutes uses 384 API calls per day (4 pages x 96 checks). The free tier includes 200 screenshots per month for testing. Paid plans cover production monitoring volumes. If you monitor specific page regions instead of full pages, the call count stays the same but comparison accuracy improves.

Written by

Vitalii Holben

Co-founder and CEO of ScreenshotRun. Full-stack developer building screenshot API tools that help developers capture, automate, and monitor web pages at scale. Based in Europe, shipping code since 201...

Screenshot API in a few clicks

Free plan includes 200 screenshots every month. No credit card required.

Sign up → copy your key → make your first call