Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use PowerShell’s Invoke-WebRequest to request each site, check its HTTP status and expected page text, record the result, and alert only after a deliberate failure policy is met. The example below targets PowerShell 7.4, uses finite connection and response timeouts, and continues monitoring other URLs when one check fails. Windows PowerShell 5.1 needs a separate timeout parameter and a security-related parsing option on affected systems.

What a useful website monitor checks

A basic “is it up?” check confirms that a URL responds. A more useful monitor also records how long the response took, verifies the status code, optionally searches for expected text, and distinguishes an HTTP error from a DNS, TLS, connection, timeout, or content failure.

PowerShell’s documented HTTP and HTTPS client is Invoke-WebRequest. Microsoft’s PowerShell 7.4 documentation describes it as a cmdlet that sends HTTP and HTTPS requests to a web page or web service. The code here is designed for PowerShell 7.4; version differences are covered below.

  • Availability: Did the request complete, and was its status the one expected?
  • Page check: Does the returned content contain a phrase that indicates the right page or service is working?
  • Performance clue: How many milliseconds elapsed? This is a measurement from the monitor’s own network location, not a universal measure of site speed.
  • Diagnosis: What status, exception, or content mismatch occurred?

A check from one machine only describes what that machine can reach. It does not prove that visitors using another network, region, or DNS resolver see the same result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
PowerShell for Sysadmins: Workflow Automation Made Easy
  • Book - powershell for sysadmins: workflow automation made easy
  • Language: english
  • Binding: paperback

Build a PowerShell 7.4 monitor

Save the following as Monitor-Websites.ps1. Replace the example URLs and expected text with values appropriate to your sites. Each target is an object, so different URLs can have different expected status codes or content assertions.

$targets = @(
    [pscustomobject]@{
        Url = 'https://example.com'
        ExpectedStatus = 200
        ExpectedText = $null
    },
    [pscustomobject]@{
        Url = 'https://www.example.org/status'
        ExpectedStatus = 200
        ExpectedText = 'Service status'
    }
)

$results = foreach ($target in $targets) {
    $started = [Diagnostics.Stopwatch]::StartNew()
    $status = $null
    $contentOk = $false
    $responseLength = $null
    $errorMessage = $null
    $errorType = $null

    try {
        $response = Invoke-WebRequest -Uri $target.Url `
            -ConnectionTimeoutSeconds 15 `
            -OperationTimeoutSeconds 30 `
            -MaximumRedirection 5 `
            -UserAgent 'SiteMonitor/1.0' `
            -ErrorAction Stop

        $status = [int]$response.StatusCode
        $content = [string]$response.Content
        $responseLength = $content.Length
        $contentOk = if ($null -ne $target.ExpectedText -and $target.ExpectedText -ne '') {
            $content.Contains([string]$target.ExpectedText)
        } else {
            $true
        }
    }
    catch {
        $errorMessage = $_.Exception.Message
        $errorType = $_.Exception.GetType().FullName

        # HTTP error responses can expose their status on the exception response.
        if ($_.Exception.Response) {
            try { $status = [int]$_.Exception.Response.StatusCode } catch { }
        }
    }
    finally {
        $started.Stop()
    }

    $healthy = ($null -eq $errorMessage -and
        $status -eq [int]$target.ExpectedStatus -and
        $contentOk)

    [pscustomobject]@{
        TimestampUtc  = [DateTime]::UtcNow.ToString('o')
        Url           = $target.Url
        StatusCode    = $status
        ElapsedMs     = [math]::Round($started.Elapsed.TotalMilliseconds, 2)
        ResponseChars = $responseLength
        ContentOk     = $contentOk
        Healthy       = $healthy
        ErrorType     = $errorType
        Error         = $errorMessage
    }
}

$results | Export-Csv -Path './website-monitor.csv' -NoTypeInformation -Append
$results | Format-Table -AutoSize

Run it from a PowerShell 7.4 session with pwsh -File ./Monitor-Websites.ps1. The CSV is appended in the current directory, and the table shows the current run. Create the CSV file’s parent directory first if you change the output path to a directory that does not exist.

How the check works

  • The target list holds a URL, the expected status, and optional expected text. Set ExpectedText to $null if only availability and status matter.
  • -ConnectionTimeoutSeconds 15 bounds connection setup, while -OperationTimeoutSeconds 30 bounds stalls during the response operation. These are different phases, not necessarily a single end-to-end deadline.
  • -MaximumRedirection 5 permits a limited number of redirects. A redirect policy violation or too many redirects becomes a failed check rather than silently following an unlimited chain.
  • -UserAgent identifies the monitor. Use a truthful, stable value; a site may handle automated clients differently.
  • -ErrorAction Stop ensures errors reach catch. Non-success HTTP responses such as 404 and 500 can throw terminating errors, so status extraction belongs in the exception path. See Microsoft’s Invoke-WebRequest documentation.
  • The result records an ISO 8601 UTC timestamp, elapsed milliseconds, status where available, response character count, content assertion, exception type, and message. The response count is characters in the decoded content, not a byte-accurate transfer size.

Check for expected text

The example uses a literal, case-sensitive String.Contains test. Choose a phrase that is stable and specific enough to distinguish the intended page, but not so fragile that a routine wording change creates an outage alert. For a case-insensitive substring check, use $content.IndexOf([string]$target.ExpectedText, [StringComparison]::OrdinalIgnoreCase) -ge 0. A successful substring check only confirms that text appears in the returned response body; it does not validate a full DOM structure, client-side rendering, or the behavior of an interactive workflow.

Make alerts useful rather than noisy

The script writes observations; it intentionally does not send an email, page someone, or treat one failed request as a confirmed outage. A transient network problem, deployment, rate limit, or remote bot check can produce a single bad sample. Apply a policy before alerting—for example, require two consecutive failures, and clear the failure streak after a healthy check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a production alert, include the URL, UTC time, failure class, last known status code, and elapsed time. Keep separate categories for HTTP status failures, content mismatches, timeouts, DNS resolution, TLS, connection refusal, and redirect errors. A caught exception’s message is useful diagnostic context but can vary by runtime and failure, so avoid making alert routing depend on parsing one exact message string.

To retain a failure streak, the script needs state across runs—such as a small state file or a monitoring system’s incident state. For email or webhook delivery, keep credentials outside the script source, limit access to them, and handle notification failures separately from the website check. Do not log authorization headers, cookies, or other secrets.

Schedule checks and keep history

A monitor only runs when its host and scheduler run. On Windows, use Task Scheduler to launch pwsh.exe with -NoProfile -File "C:pathMonitor-Websites.ps1"; configure the task’s working directory or use an absolute CSV path because scheduled tasks may start in a different directory than an interactive shell. On Linux or macOS, schedule pwsh -File /path/Monitor-Websites.ps1 with cron or an automation runner, and use absolute paths for the script and log.

Pick an interval based on how quickly you need to detect an issue and the load the checks create. If there are many URLs, sequential requests mean a slow target can extend the run; for a small list this is simple and easy to reason about. Preserve logs long enough to identify recurring failures and compare duration trends, while rotating or archiving the CSV so it does not grow indefinitely.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A local script is transparent and customizable, but normally measures from one network location. A hosted monitor may provide independent vantage points, managed history, retry controls, and alert integrations; compare those capabilities, credential handling, retention, and total operating cost against the maintenance of your own scheduler and logs. A published PowerShell cookbook also includes a website uptime monitor example, illustrating this as a practical scripting pattern: Windows PowerShell Cookbook, Third Edition.

PowerShell version and timeout differences

PowerShell 7.4

Use -ConnectionTimeoutSeconds and -OperationTimeoutSeconds as shown. Microsoft documents the first for connection setup and the second for stalls while reading a response. Set finite values that fit the endpoint and detection needs; no small timeout should be treated as a guarantee that every failed request returns within exactly that number of seconds.

Windows PowerShell 5.1

Windows PowerShell 5.1 uses the older -TimeoutSec parameter rather than the two 7.4 timeout parameters. Set it to a finite value; Microsoft documents a default of zero (no timeout). The 5.1 documentation also warns that DNS resolution can take up to 15 seconds, so a timeout below 15 seconds may still take 15 seconds or longer before a timeout exception appears. See the Windows PowerShell 5.1 cmdlet documentation.

For an affected Windows PowerShell 5.1 installation, Microsoft Support says a security update released December 9, 2025 changes default Invoke-WebRequest behavior by warning about script execution risk when web content is parsed. If the script only fetches response content and does not need advanced DOM parsing, add -UseBasicParsing to the request on those systems. Consult Microsoft Support’s update guidance for the applicable environment and details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

  • The script hangs or takes longer than expected: Confirm you are using finite timeout parameters supported by the PowerShell version in use. DNS can exceed a very short timeout in Windows PowerShell 5.1; investigate name resolution separately rather than assuming the server itself is slow.
  • A 404 or 500 appears as an exception: This is expected behavior for non-success responses. Keep -ErrorAction Stop and read the response status in catch, as the example does. A blank status can mean the failure happened before an HTTP response was available.
  • Every check fails with a name-resolution or connection error: Test the URL from the same host and account that runs the scheduled task. Check DNS, proxy settings, firewall rules, and network reachability. A successful browser request on another machine does not establish that this machine has the same route.
  • TLS negotiation fails: Check the machine’s trust store, certificate validity and hostname, and runtime environment. -SslProtocol can select TLS protocols, but do not restrict protocols unless a compliance requirement or endpoint compatibility issue requires it; protocol restrictions can make otherwise valid connections fail. See the cmdlet’s TLS parameter documentation.
  • The result is unhealthy despite a 200: Inspect ContentOk and the expected phrase. The page may have changed, returned a maintenance page, or rely on JavaScript to add text that is not present in the initial response.
  • The scheduled task produces no CSV or writes it elsewhere: Use an absolute output path and verify the task account can write to its directory. Scheduled tasks do not necessarily inherit the current interactive shell’s working directory or profile.
  • The site blocks the request or shows a bot check: The endpoint may treat the monitor differently from an ordinary visitor. Do not interpret a bot challenge as proof the site is globally down; record it as a distinct response and consider a monitoring vantage point or method appropriate to the site’s access policy.
  • Alerts fire during brief blips: Add consecutive-failure logic, a recovery notification, and a reasonable check interval. Avoid hiding recurring failures by adding long sleeps or retries that make detection much slower.

Or skip the browser setup

If your goal is a clean screenshot or PDF of a page rather than an availability check, ScreenshotNeo is a website screenshot API and MCP server. A PowerShell uptime script checks HTTP response behavior; it does not itself render a reliable browser screenshot. ScreenshotNeo’s single GET request returns an image or PDF, and its capture options include full-page output, element selection, viewport and device settings, waits, and custom headers.

With an API key, the basic cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

See the ScreenshotNeo API documentation for parameters and response details. Cookie and consent banners are accepted and removed before capture, along with supported newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers indicate the page verdict and billing result. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Sign up for free ScreenshotNeo access.

Frequently Asked Questions

Can a PowerShell website check prove that a site is available to everyone?

No. A script running from one host measures reachability from that host’s network location; it cannot establish availability from every visitor’s region or provider.

Does Invoke-WebRequest execute a page like a full browser?

No. It makes an HTTP or HTTPS request and exposes the response; a text assertion does not verify JavaScript-rendered content or interactive browser behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.