Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python website uptime monitoring

Full vendor comparison (pricing, cost traps, 7 tools):
Best Website Monitoring Tools in 2026

This repo is a small Python toolkit for website uptime checks: HTTP (with keyword), SSL expiry, DNS, and TCP. Run the scripts locally to see what “healthy” actually means — status codes alone are not enough — then use the blog when you outgrow a laptop cron and need multi-region probes, alerting, and a status page.

What this repo does

  • Teaches assertion layers with runnable examples (stdlib-first)
  • Uses devhelm.io as the default live target
  • Stays out of tool rankings and pricing tables (that lives on the blog)

What this repo does not do

  • Replace a managed monitoring product
  • Rank DevHelm vs UptimeRobot vs Checkly, etc.

Layout

Path Role
uptime/ Reusable classes (HttpCheck, SslCheck, DnsCheck, TcpCheck, BlindWindow, CheckSuite, ResultPrinter)
examples/ Thin CLIs — parse args, run a check, print via ResultPrinter
targets.example.yaml Sample suite aimed at DevHelm

Checks return a silent CheckResult. All printing goes through ResultPrinter, so probes are reusable without stdout noise.

flowchart LR
  CLI[examples/*.py] --> Check[Check.run]
  Check --> Result[CheckResult]
  Result --> Print[ResultPrinter]
  YAML[targets.yaml] --> Suite[CheckSuite]
  Suite --> Check
Loading

Quick start

git clone https://github.com/devhelmhq/python-website-uptime-monitoring.git
cd python-website-uptime-monitoring
pip install -r requirements.txt

# Full DevHelm example suite
python examples/run_suite.py --config targets.example.yaml

Expected (when the site is up): all checks PASS, exit code 0, summary like 4/4 passed.

Reuse in your own code

from uptime import HttpCheck, ResultPrinter

result = HttpCheck("https://devhelm.io", keyword="DevHelm").run()
ResultPrinter().result(result)

Theory: what website uptime checks are

Uptime monitoring is a schedule of probes from outside your network that ask: can a remote client reach this hostname, port, or URL, and does the response match what you expect?

It is not the same as looking at your dashboard inside the VPC. Pods can be “Running,” /health can return 200 on localhost, and users can still see a blank page, a TLS warning, or a dead DNS answer. External checks close that gap.

Managed products (the ones compared in the blog) wrap these probe types, run them from many regions, alert you, and often drive a status page. This repo isolates the probe ideas so you can see what each type is actually verifying.

Why several check types exist

A website request fails in layers. One green layer does not prove the next:

flowchart TB
  U[User / probe] --> DNS[DNS resolve]
  DNS --> TCP[TCP connect]
  TCP --> TLS[TLS / SSL]
  TLS --> HTTP[HTTP status + body]
  DNS -.->|fail| E1[Wrong or missing IP]
  TCP -.->|fail| E2[Port closed / filtered]
  TLS -.->|fail| E3[Cert expired / untrusted]
  HTTP -.->|fail| E4[Bad status or wrong content]
Loading
flowchart LR
  subgraph Layers
    D[DNS]
    T[TCP]
    S[SSL]
    H[HTTP + keyword]
  end
  D -->|IP known| T
  T -->|port open| S
  S -->|HTTPS ok| H
  H -->|keyword match| OK[Usable site]
Loading
If only this is green… Users can still see…
DNS Wrong or unreachable IP after a cutover
TCP Open port serving nothing useful / wrong process
SSL Expired or mismatched cert → browser blocks them
HTTP status 200 Empty body, error page HTML, wrong site

That is why serious uptime setups stack assertions (status + body keyword + TLS days left + DNS), not a single ping.

Check layers (map to scripts)

Layer Script What it checks What it is for
DNS dns_check.py Name resolves (optionally to expected IPs) Catch zone / registrar / cutover mistakes
TCP tcp_check.py Port accepts a TCP connection Catch firewall, process down, wrong bind
SSL ssl_check.py Cert notAfter far enough in the future Catch expiry before Chrome does
HTTP + keyword http_check.py Status code + optional body substring + latency Catch “up but wrong/broken page”
Interval math blind_window.py Max time an outage can hide between probes Choose check frequency deliberately
Suite run_suite.py All of the above from YAML Rehearse “monitoring as config” locally
flowchart TD
  R[User reports down] --> Q{What do they see?}
  Q -->|Name not found / wrong host| DNS[dns_check.py]
  Q -->|Connection timed out| TCP[tcp_check.py]
  Q -->|Not private / TLS error| SSL[ssl_check.py]
  Q -->|Blank or wrong page| HTTP[http_check.py]
  DNS --> Fix1[Zone / registrar / cutover]
  TCP --> Fix2[Firewall / process / bind]
  SSL --> Fix3[Renew cert / ACME]
  HTTP --> Fix4[App / deploy / CDN]
Loading

When you need regions, Slack/PagerDuty, and a public status page from the same probe data, compare platforms here: best website monitoring tools.


examples/http_check.py

Theory

What an HTTP check is: a scheduled GET (or other method) to a URL that asserts properties of the HTTP response, not merely that a socket opened.

What it is for: answering “does the site respond correctly for a client?” — the closest simple proxy for “is the homepage / health URL usable.”

What it checks in this script:

Assertion Meaning
Status code ∈ allow-list Server reached application logic (or at least returned an expected code)
Keyword in body (optional) Response is the page you expect, not an empty shell, CDN error HTML, or wrong vhost
Latency (reported) How long the round trip took (soft signal; not a hard fail here)

Why keyword matters: TCP connect or bare 200 can still serve a broken or wrong page. A keyword (e.g. your product name) is the simplest content assertion. Deeper stacks add JSON path, header, and multi-step API checks — see the blog’s monitor-type matrix.

flowchart LR
  A[GET url] --> B{Status allowed?}
  B -->|no| F[FAIL]
  B -->|yes| C{Keyword set?}
  C -->|no| P[PASS]
  C -->|yes| D{Keyword in body?}
  D -->|yes| P
  D -->|no| F
Loading

What the script does

GET a URL, assert status is allowed, optionally require a keyword in the body, print latency, exit 0/1.

Run (DevHelm)

python examples/http_check.py
# same as:
python examples/http_check.py https://devhelm.io --keyword DevHelm --expect-status 200

Expected result (healthy)

PASS  https://devhelm.io
  status: 200 (expected [200])
  latency_ms: <number>
  keyword: 'DevHelm' -> found

Exit code: 0.

Status-only (no keyword)

python examples/http_check.py https://devhelm.io --no-keyword

Deliberate fail (wrong keyword)

python examples/http_check.py https://devhelm.io --keyword "this-string-is-not-on-the-page"

Expected: FAIL, keyword: ... -> MISSING, exit 1.


examples/ssl_check.py

Theory

What an SSL/TLS check is: a probe that completes (or inspects) the TLS handshake and evaluates the server certificate — especially how soon it expires.

What it is for: catching certificate problems before users hit “Your connection is not private.” The app process and even HTTP-on-another-port can look fine while HTTPS is unusable.

What it checks in this script:

Assertion Meaning
TLS handshake succeeds Client can negotiate HTTPS to this host
notAfter − now ≥ min_days Enough calendar time left before expiry (e.g. 14 days)
Common name (reported) Which identity the cert claims (informational here)

What it does not fully replace: full PKI validation policies, OCSP stapling checks, or “cert matches this exact SAN list” audits. Managed tools often add those as optional assertions; this script teaches the core expiry idea.

flowchart LR
  N[now] --> L[days_left]
  E[notAfter] --> L
  L --> T{days_left >= min_days?}
  T -->|yes| P[PASS]
  T -->|no| F[FAIL - renew soon]
Loading

What the script does

Opens TLS to host:443, reads certificate notAfter, fails if fewer than --min-days remain.

Run (DevHelm)

python examples/ssl_check.py
# same as:
python examples/ssl_check.py devhelm.io --port 443 --min-days 14

Expected result (healthy)

PASS  devhelm.io:443
  not_after: <date in the future>
  days_left: <number >= 14>
  common_name: <cert CN>

Exit code: 0.

Stricter threshold

python examples/ssl_check.py devhelm.io --min-days 3650

Likely FAIL if the cert has less than ~10 years left — useful to see a failure shape without waiting for real expiry.


examples/dns_check.py

Theory

What a DNS check is: a probe that asks a resolver for records for a hostname (typically A/AAAA) and asserts that resolution succeeds — optionally that answers match an expected set of IPs.

What it is for: separating “the site is down” from “the name no longer points where you think.” Classic symptoms: works on office VPN, fails at home; old load-balancer IP still in cache after a cutover; registrar or zone edit mistakes.

What it checks in this script:

Assertion Meaning
getaddrinfo succeeds with ≥1 address The name resolves from this machine’s resolver
Optional --expect IPs present Answers still include the IP(s) you intend

Why it is separate from HTTP: an HTTP check against a stale IP can keep passing while new users resolve to a dead address. DNS monitors watch the name itself.

sequenceDiagram
  participant P as Probe
  participant R as Resolver
  participant Z as Authoritative zone
  P->>R: lookup devhelm.io
  R->>Z: query A/AAAA
  Z-->>R: addresses
  R-->>P: IPs
  Note over P: Optional: assert expected IPs present
Loading

What the script does

Resolves a hostname; fails if resolution fails or (optionally) expected IPs are missing. By default any non-empty answer passes.

Run (DevHelm)

python examples/dns_check.py
# same as:
python examples/dns_check.py devhelm.io

Expected result (healthy)

PASS  devhelm.io
  addresses: <one or more IPs>

Exit code: 0.

Optional IP assert

python examples/dns_check.py devhelm.io --expect 203.0.113.10

Expected if that IP is not live for the host: FAIL with missing: ....


examples/tcp_check.py

Theory

What a TCP (port) check is: a probe that tries to open a TCP connection to host:port and succeeds if the handshake completes within a timeout.

What it is for: answering “is anything listening and reachable on this port?” Useful for non-HTTP services (databases, SMTP, custom TCP) and as a low-level signal when HTTP is not the right protocol.

What it checks in this script:

Assertion Meaning
connect() succeeds Path to host + port is open; something accepted the connection
Latency (reported) Time to establish the TCP session

What it does not check: TLS validity, HTTP status, response body, or application health. A port can be open and still serve the wrong process or garbage data — always pair with HTTP/SSL when the product is a website.

flowchart TB
  TCP[TCP PASS] --> Q{Also check HTTP?}
  Q -->|no| Risk[Miss wrong / empty body]
  Q -->|yes| HTTP[HTTP + keyword]
  HTTP --> Safe[Port open AND page correct]
Loading

What the script does

Connects to host:port with a timeout. Pass = port accepts connections. Use beside http_check.py on the same host to see the gap between “port open” and “page correct.”

Run (DevHelm)

python examples/tcp_check.py
# same as:
python examples/tcp_check.py devhelm.io --port 443

Expected result (healthy)

PASS  devhelm.io:443
  latency_ms: <number>

Exit code: 0.

Contrast

python examples/tcp_check.py devhelm.io --port 443
python examples/http_check.py https://devhelm.io --keyword DevHelm

Both should pass for a healthy site. If you only ran TCP, you would miss a wrong HTML body.


examples/blind_window.py

Theory

What check interval means: how often a monitor runs. If you check every 5 minutes, an outage that starts just after a successful probe can stay undetected for almost a full interval.

What the blind window is: that worst-case undetected duration. For a fixed interval (I), max blind window ≈ (I) (fail immediately after a pass; next probe is (I) later).

What it is for: choosing frequency on purpose. Faster intervals shrink MTTR from detection; they do not fix slow repairs. Free tiers often use 3–5 minute intervals; paid plans advertise 30s or less — that difference is detection latency, which this script makes numeric. Pricing of those intervals belongs on the comparison article.

%%{init: {"theme": "neutral"}}%%
xychart-beta
  title "Max blind window vs check interval"
  x-axis ["10s", "30s", "60s", "5m", "15m"]
  y-axis "Blind window (seconds)" 0 --> 900
  bar [10, 30, 60, 300, 900]
Loading
sequenceDiagram
  participant M as Monitor
  participant S as Site
  Note over M,S: interval = 5m
  M->>S: probe OK (t=0)
  Note over S: outage starts (t=10s)
  Note over M,S: blind window - still undetected
  M->>S: probe FAIL (t=300s)
  Note over M: detected after ~290s
Loading

What the script does

No network. Prints interval → blind window, or simulates fail-at / detect-at for one scenario.

Run

python examples/blind_window.py

Expected result

Check interval -> max blind window (worst case: fail just after a probe)

interval   blind_window
------------------------
10s        10s
30s        30s
60s        1.0m
5m         5.0m
15m        15.0m

Simulate one outage

python examples/blind_window.py --interval 300 --fail-at 10

Expected: failure at t=10s, next 5m probe detects at t=300s, blind window 290s (shown as minutes).

For how tools price and expose those intervals, see best website monitoring tools.


examples/run_suite.py + targets.example.yaml

Theory

What a check suite is: several probe types aimed at the same product, run together so one green TCP check cannot hide a bad cert or empty homepage.

What “monitoring as code” means here: targets live in a file (YAML) next to the scripts — same idea as keeping monitors in Git in a real platform. Change the URL in a PR-shaped edit; re-run the suite. Managed tools add deploy APIs, multi-region workers, and alert routing on top of this idea.

What the suite is for: local rehearsal of the layers you would buy later (DNS + TCP + SSL + HTTP), not production on-call coverage from one laptop.

flowchart TB
  Y[targets.yaml] --> S[CheckSuite]
  S --> D[DnsCheck]
  S --> T[TcpCheck]
  S --> L[SslCheck]
  S --> H[HttpCheck]
  D --> R[CheckResult]
  T --> R
  L --> R
  H --> R
  R --> P[ResultPrinter]
  P --> Sum{All PASS?}
  Sum -->|yes| Exit0[exit 0]
  Sum -->|no| Exit1[exit 1]
Loading

What the script does

Loads a YAML list of checks and runs HTTP / SSL / DNS / TCP in one go. Exit 1 if any check fails.

Config (DevHelm examples)

targets.example.yaml already points at devhelm.io:

  • homepage HTTP + keyword DevHelm
  • SSL with 14-day floor
  • DNS resolve
  • TCP :443

Run

pip install -r requirements.txt
python examples/run_suite.py --config targets.example.yaml

Expected result (healthy)

Running 4 check(s) from targets.example.yaml

--- devhelm-homepage (http) ---
PASS  https://devhelm.io
  ...

--- devhelm-ssl (ssl) ---
PASS  devhelm.io:443
  ...

--- devhelm-dns (dns) ---
PASS  devhelm.io
  ...

--- devhelm-https-port (tcp) ---
PASS  devhelm.io:443
  ...

Summary: 4/4 passed, 0 failed

Exit code: 0.

Custom targets

cp targets.example.yaml targets.yaml
# edit URLs / hosts
python examples/run_suite.py --config targets.yaml

Laptop vs managed: a suite on one machine has no multi-region confirmation, no on-call routing, and no public status page. When you need those, compare products in Best Website Monitoring Tools in 2026.


Requirements

Dependency Needed for
Python 3.10+ All scripts
PyYAML (requirements.txt) run_suite.py only

Individual scripts (http_check, ssl_check, dns_check, tcp_check, blind_window) use the standard library only.


License

MIT — see LICENSE.

About

Python HTTP, SSL, DNS, and TCP website uptime checks — learn assertions, then compare managed tools

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages