A lightweight Python CLI that periodically warms selected website URLs.
Project description
sitewarmer
Lightweight Python CLI for warming and checking selected website URLs without turning into a load tester.
What It Does
sitewarmer periodically requests one or more URLs on a site to help keep small or shared-hosted sites responsive, confirm uptime, and improve perceived first-load responsiveness for real users.
It can:
- warm explicit URLs
- discover more URLs from
sitemap.xml - discover internal links from the homepage
- enforce conservative rate limits
- respect
robots.txt - output JSON or readable terminal summaries
- append local JSONL logs
What It Does Not Do
This is not:
- a load tester
- a stress tester
- a DDoS tool
- a scraper farm
- a bypass tool for robots or access controls
Ethical Use
Use this only on websites you own, operate, or are explicitly authorized to check. Keep intervals conservative. Do not use it to generate aggressive traffic.
Features
- One or more target URLs
- One-shot mode
- Continuous mode
- Dry-run mode
- Configurable interval and timeout
- Retry limits
- Custom
User-Agent - Sitemap discovery
- Internal homepage link discovery
- Bounded same-site link depth with
--max-depth - Discovery result limiting
- Include/exclude patterns
- JSON output
- Plain terminal summaries
- Local JSONL log file support
- Graceful
Ctrl+Cshutdown - Safe URL validation
- External-domain blocking by default
- Robots-aware fetch decisions
Installation
cd /path/to/sitewarmer
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
No third-party runtime dependencies are required.
Quick Start
Warm one URL once:
sitewarmer https://example.com --once
Warm continuously every 5 minutes:
sitewarmer https://example.com --interval 300
sitewarmer runs continuously by default. Add --once when you want a single cycle.
Discover sitemap URLs first:
sitewarmer https://example.com --discover sitemap --limit 25
Discover sitemap URLs and homepage links together:
sitewarmer https://example.com --discover both --limit 25
Discover internal homepage links:
sitewarmer https://example.com --discover links --max-depth 1 --exclude "/admin" --exclude "/cart"
JSON output:
sitewarmer https://example.com --once --json
Write a log file:
sitewarmer https://example.com --log-file warmer.log
CLI Reference
| Option | Meaning |
|---|---|
urls |
One or more target URLs |
--interval SECONDS |
Delay between cycles in continuous mode. Default: 300 |
--timeout SECONDS |
Request timeout. Default: 10 |
--retries N |
Retry count for transient failures. Default: 2 |
--user-agent UA |
Custom User-Agent string |
| `--discover none | sitemap |
--limit N |
Maximum discovered URLs to add per run. Default: 10 |
--max-depth N |
Maximum same-site link hops beyond the homepage for links or both discovery. Default: 0 |
--include PATTERN |
Include filter. Repeatable |
--exclude PATTERN |
Exclude filter. Repeatable |
--allow-external |
Allow external discovered URLs |
--dry-run |
Plan the run without requests |
--once |
Run one cycle and exit |
--continuous |
Keep running until interrupted. This is also the default when --once is not used |
--json |
Emit JSON output |
--log-file PATH |
Append JSONL event logs to a file |
--read-limit BYTES |
Max bytes read per response. Default: 262144 |
--request-delay SECONDS |
Minimum per-host delay inside a cycle. Default: 1.0 |
-v, --verbose |
Verbose plain-text logging |
Example Output
[2026-05-17T00:00:00Z] mode=once targets=2 ok=2 failed=0 skipped=0
OK https://example.com/ status=200 time=143ms bytes=24.0KiB
OK https://example.com/blog status=200 time=98ms bytes=18.2KiB
JSON example:
{
"finished_at_utc": "2026-05-17T00:00:00Z",
"interval_seconds": 300,
"mode": "once",
"results": [
{
"attempts": 1,
"bytes_read": 24576,
"elapsed_ms": 143.2,
"error": null,
"final_url": "https://example.com/",
"ok": true,
"origin": "https://example.com/",
"skipped": false,
"source": "seed",
"status_code": 200,
"url": "https://example.com/"
}
],
"started_at_utc": "2026-05-17T00:00:00Z",
"summary": {
"bytes_read": 24576,
"failed": 0,
"ok": 1,
"skipped": 0,
"total": 1
},
"targets": [
"https://example.com/"
]
}
Sitemap Discovery
--discover sitemap checks robots.txt for sitemap hints, then tries the site’s sitemap candidates, including site_maps() support from Python’s urllib.robotparser, and finally /sitemap.xml as a fallback.
Only same-site URLs are kept unless --allow-external is explicitly set.
Link Discovery
--discover links fetches the homepage and extracts internal links from anchor tags. By default it stays one hop wide from the homepage. Raise --max-depth to let it follow a bounded number of same-site hops, and keep --limit low for small sites.
--discover both combines sitemap discovery and link discovery in one pass. This is the most useful mode for small, stable sites that already have a sitemap but also expose a few important internal links from the homepage.
Safety and Rate Limiting
- Default interval:
300seconds - Default request timeout:
10seconds - Default retries:
2 - Default per-host delay inside a cycle:
1second - External URLs are blocked unless
--allow-externalis given robots.txtis checked before warming targets--dry-runperforms no requests
If you need something more aggressive than that, you are outside the intended use case.
Cron Example
*/5 * * * * /path/to/sitewarmer/.venv/bin/sitewarmer https://example.com --once --log-file /var/log/sitewarmer.jsonl
systemd Timer Example
/etc/systemd/system/sitewarmer.service
[Unit]
Description=SiteWarmer one-shot check
[Service]
Type=oneshot
ExecStart=/path/to/sitewarmer/.venv/bin/sitewarmer https://example.com --once --log-file /var/log/sitewarmer.jsonl
/etc/systemd/system/sitewarmer.timer
[Unit]
Description=Run SiteWarmer every 5 minutes
[Timer]
OnBootSec=1m
OnUnitActiveSec=5m
Unit=sitewarmer.service
[Install]
WantedBy=timers.target
Logging
Use --log-file to append JSONL events. Each run records timestamped URL results with status, response time, bytes read, and error reason when present.
Use Cases
Shared Hosting
Keep a small site responsive without hammering the host. Use a conservative interval and sitemap discovery only if the site is small and stable.
Small Business Site
Warm the homepage, contact page, services page, or a short sitemap list so the first real customer visit does not hit a cold start.
Developer or Staging Site
Confirm uptime during demos, deployments, or maintenance windows without introducing noisy traffic.
Troubleshooting
invalid URL: includehttps://or a valid host namerobots.txt blocks a page: the tool is respecting the site rulesNo output in continuous mode: check--json,--verbose, or--log-fileToo many discovered URLs: lower--limitor narrow--include/--excludeUnexpected external URLs: remove--allow-external
Testing
python3 -m unittest discover -s tests
python3 -m compileall sitewarmer tests
python3 -m sitewarmer https://example.com --once
Roadmap
- optional per-host scheduling
- richer status summaries
- package publishing
- release tagging and changelog workflow
- explicit sitemap index reporting
Contributing
Keep changes small, safe, and readable. Do not add aggressive crawling, scraping, or load generation.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sitewarmer-0.1.1.tar.gz.
File metadata
- Download URL: sitewarmer-0.1.1.tar.gz
- Upload date:
- Size: 18.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff11d1387f03633f018650c441c6f56ce48e27dca886686ad4d0651225902e05
|
|
| MD5 |
4f47f429c73bdc5c3166363c25577d7b
|
|
| BLAKE2b-256 |
507ddab652f4460088cc554183bcf2defe38a0b2d00746db4733b1ee05cd1de5
|
Provenance
The following attestation bundles were made for sitewarmer-0.1.1.tar.gz:
Publisher:
release.yml on Clock-Skew/Sitewarmer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sitewarmer-0.1.1.tar.gz -
Subject digest:
ff11d1387f03633f018650c441c6f56ce48e27dca886686ad4d0651225902e05 - Sigstore transparency entry: 1556900119
- Sigstore integration time:
-
Permalink:
Clock-Skew/Sitewarmer@ba641822bbf5ab708eff12fe1ba31d5d233fdf4b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Clock-Skew
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ba641822bbf5ab708eff12fe1ba31d5d233fdf4b -
Trigger Event:
release
-
Statement type:
File details
Details for the file sitewarmer-0.1.1-py3-none-any.whl.
File metadata
- Download URL: sitewarmer-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a9a3567fc8dbb9a2f951d32e4b9cb1b6e4d00e0dee514b84ed587f311fe3024
|
|
| MD5 |
987da0659395d706d8b5634550747738
|
|
| BLAKE2b-256 |
25677f271ad7e4f6a2c6e4e793bee2fba4159ce558f701eb6b0643859c570615
|
Provenance
The following attestation bundles were made for sitewarmer-0.1.1-py3-none-any.whl:
Publisher:
release.yml on Clock-Skew/Sitewarmer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sitewarmer-0.1.1-py3-none-any.whl -
Subject digest:
1a9a3567fc8dbb9a2f951d32e4b9cb1b6e4d00e0dee514b84ed587f311fe3024 - Sigstore transparency entry: 1556900288
- Sigstore integration time:
-
Permalink:
Clock-Skew/Sitewarmer@ba641822bbf5ab708eff12fe1ba31d5d233fdf4b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Clock-Skew
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ba641822bbf5ab708eff12fe1ba31d5d233fdf4b -
Trigger Event:
release
-
Statement type: