Regenerate your sitemap from the finished public page set whenever you publish, then upload both together. This Mac workflow uses a small Python script to include added pages and drop removed ones automatically, with checks for the public result.
The task here is maintaining a sitemap for a small static website exported from a Mac. The example uses synthetic pages about colors. It assumes your existing publishing process can upload the export folder and remove obsolete files from the public site.
Choose where sitemap regeneration belongs
First check whether your publishing system already generates a sitemap. Google notes that many content management systems provide one automatically. If yours does, check that sitemap after adding and removing a test page before introducing another generator. Learn about sitemaps.
For a static export, use this sequence: finish exporting the pages, generate the sitemap from that export, and publish the resulting folder. This is a proposed implementation pattern: it makes the sitemap describe the same page set that the publisher receives.
Generating during publication also gives additions and removals one consistent rule. A page present in the finished export is included; a page absent from it is omitted. There is no separate list to remember to update.
Choose the integration that matches your existing setup:
| Existing publishing setup | Where regeneration belongs |
|---|---|
| A CMS already maintains the sitemap | Keep that generator and test its response to page changes |
| A static exporter supports a command after export | Run the generator there, before upload |
| An existing command publishes a finished folder | Run it through the generator’s optional publishing argument |
| You upload through a desktop interface | Generate first, then upload the complete folder; unattended public updating requires a supported publishing integration |
A local script cannot change a remote website by itself. The workflow below automates sitemap generation and can launch an existing publishing command. The actual upload remains the responsibility of your established publisher.
Define exactly which pages belong in the sitemap
For ordinary page URLs, Google accepts a text sitemap containing one complete URL per line. XML is optional for this task. Include the preferred public URL for each page rather than multiple URL versions of the same content. Build and submit a sitemap.
The text format requires UTF-8 encoding and contains only URLs, without headings or comments. A single file can contain at most 50,000 URLs and 52,428,800 bytes. Put this example’s sitemap at the site root so it covers the exported pages beneath that location. Sitemaps protocol: text files and location.
The generator uses a deliberately simple export convention:
| Exported file | Illustrative public URL |
|---|---|
public/index.html |
https://example.com/ |
public/colors/blue.html |
https://example.com/colors/blue.html |
public/tiles/index.html |
https://example.com/tiles/ |
Use this convention only if your host serves those exact URLs. If your publisher turns blue.html into /colors/blue/, or serves the site beneath a path such as /collection/, use its route list or adapt the mapping before publishing. A filename scan cannot infer custom routing.
Keep only pages intended for inclusion in the export folder. This example skips dot-prefixed paths and files named 404.html; it does not inspect page metadata, canonical declarations, or redirect rules. Sites with those additional conditions should generate from their publisher’s eligible-page inventory instead.
Continue maintaining normal navigation links as well. WHATWG defines hyperlinks created by a elements with an href attribute, and Google explains that properly linked pages can usually be discovered through navigation. Updating a sitemap should accompany updates to links pointing at added or removed pages. WHATWG HTML Standard: links, Learn about sitemaps.
Create the generator on your Mac
You need Python 3.8 or later and a plain-text editor. In Terminal, run python3 --version to check the interpreter available to you. If you need Python, follow the Python Software Foundation’s macOS installation instructions. Its documentation also describes running scripts with python3 from Terminal. Using Python on macOS.
Create a folder named sitemap-demo on your Desktop. Save the following complete program inside it as regenerate_sitemap.py. The program uses Python’s standard library and requires no additional packages.
import argparse
from pathlib import Path, PurePosixPath
import subprocess
import tempfile
from urllib.parse import quote, urlsplit
def make_sitemap(paths, origin):
parsed = urlsplit(origin)
if (parsed.scheme not in ("http", "https") or not parsed.hostname
or parsed.netloc != parsed.hostname
or parsed.path not in ("", "/")
or parsed.query or parsed.fragment):
raise ValueError("Use a site origin, such as https://example.com")
base = origin.rstrip("/")
urls = set()
for relative in paths:
path = PurePosixPath(relative)
if path.name == "index.html":
parent = path.parent.as_posix()
route = "" if parent == "." else parent + "/"
else:
route = path.as_posix()
urls.add(base + "/" + quote(route, safe="/"))
if not urls:
raise ValueError("No eligible pages; sitemap was not replaced")
if len(urls) > 50000:
raise ValueError("Split the sitemap: more than 50,000 URLs")
data = ("\n".join(sorted(urls)) + "\n").encode("utf-8")
if len(data) > 50 * 1024 * 1024:
raise ValueError("Split the sitemap: larger than 50 MB")
return data
def main():
parser = argparse.ArgumentParser()
parser.add_argument("folder")
parser.add_argument("origin")
parser.add_argument("publish", nargs=argparse.REMAINDER)
args = parser.parse_args()
root = Path(args.folder).resolve()
if not root.is_dir():
parser.error("The export folder must already exist")
paths = []
for file in root.rglob("*.html"):
relative = file.relative_to(root)
if (file.is_file() and file.name != "404.html"
and not any(p.startswith(".") for p in relative.parts)):
paths.append(relative.as_posix())
data = make_sitemap(paths, args.origin)
target = root / "sitemap.txt"
temporary = None
try:
with tempfile.NamedTemporaryFile(dir=root, delete=False) as handle:
temporary = Path(handle.name)
handle.write(data)
temporary.replace(target)
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
print(f"Wrote {len(data.splitlines())} URLs to {target}", flush=True)
if args.publish:
subprocess.run(args.publish, check=True)
if __name__ == "__main__":
main()
Each run reconstructs the whole URL list. It sorts and deduplicates the result, encodes characters such as spaces in paths, and writes a temporary file before replacing sitemap.txt. Generation errors stop the program before the optional publishing command runs.
An empty export deliberately raises an error and leaves the previous sitemap untouched. That catches an accidentally selected or incomplete folder. Intentionally removing every page requires a separate publishing decision rather than passing an empty export through this workflow.
Test an addition and a removal before connecting publishing
In Terminal, move into your demonstration folder:
cd "$HOME/Desktop/sitemap-demo"
Create two synthetic pages with this complete setup command. Use it in the new demonstration folder; it writes the two named sample files.
python3 - <<'PY'
from pathlib import Path
root = Path("public")
(root / "colors").mkdir(parents=True, exist_ok=True)
pages = {
"index.html": "<!doctype html><title>Color collection</title><p>Color collection.</p>",
"colors/blue.html": "<!doctype html><title>Blue tile</title><p>A blue tile.</p>",
}
for name, content in pages.items():
(root / name).write_text(content, encoding="utf-8")
PY
Generate and inspect the local sitemap. The example origin is illustrative; these commands do not publish anything to it.
python3 regenerate_sitemap.py public https://example.com
cat public/sitemap.txt
The expected file contains exactly these two lines:
https://example.com/
https://example.com/colors/blue.html
Now replace the synthetic blue page with a green page:
python3 - <<'PY'
from pathlib import Path
root = Path("public")
(root / "colors/blue.html").unlink()
(root / "colors/green.html").write_text(
"<!doctype html><title>Green tile</title><p>A green tile.</p>",
encoding="utf-8",
)
PY
python3 regenerate_sitemap.py public https://example.com
cat public/sitemap.txt
The expected result is the home URL followed by https://example.com/colors/green.html. The blue URL disappears because its exported file is gone. This checks both directions of the update without relying on a search engine to notice the change.
Make regeneration part of every publication
For your real site, replace the demonstration folder and origin with your actual finished export folder and public origin. Run the generator only after the export has completed, and keep that folder unchanged until publication finishes. Run publications sequentially.
If you already have a working publishing script named deploy-site.sh in the project folder, the following integration command generates the sitemap and then runs that script. This example requires the existing script; the generator does not create it or supply hosting configuration.
python3 regenerate_sitemap.py public https://example.com sh ./deploy-site.sh
Your publishing script must upload the same public folder, including sitemap.txt, and remove public files that disappeared from the export. It must not rebuild the pages after sitemap generation. For a publisher that also builds the site, place generation between its build and upload stages instead.
If generation fails, the publishing command is skipped. If publishing fails, Python reports the failure, but the remote site may already be partly changed. Check the public result before retrying. Where your host supports publishing a complete release together, use that documented mechanism.
For a desktop uploader, generate the sitemap before selecting the export folder for upload. Confirm that the uploader replaces the existing sitemap and handles removed pages. This still requires the upload action; automatic local generation alone does not provide unattended public publication.
Verify the public sitemap and removed page separately
After a successful publication, open your actual public sitemap.txt URL in a browser. Check that the added page appears and the removed page does not. Then visit each affected page directly. This distinguishes a correct local export from a correct public release.
Deleting a sitemap entry does not determine the HTTP response at that page’s old address. For a removed page with no replacement, check that the host returns an appropriate missing-page response. RFC 9110 defines 404 for a resource without a current representation and 410 when its unavailability is known to be likely permanent. RFC 9110: HTTP Semantics.
A page that displays a missing-page message while returning 200 can be treated as a soft 404. Google also documents that previously indexed URLs returning applicable 4xx responses are removed from its index over time. Removing the URL from a sitemap is therefore only one part of removing the page. How HTTP status codes affect Google's crawlers.
Use this diagnostic sequence when something looks wrong:
| Observation | Next check |
|---|---|
| The local sitemap still lists a removed page | Look for a leftover exported file or the wrong export folder |
| The local sitemap is correct, but the public copy differs | Check the uploaded folder, destination, and caching |
| The public sitemap is correct, but the removed page still opens | Check whether publication removed the old remote file |
| A generated URL opens the wrong page | Check the host’s route mapping against the filename convention |
| A new URL is public but absent from search | Treat search discovery and indexing as a separate process |
Caching can explain why a public sitemap still appears unchanged. RFC 9111 defines how cached responses remain fresh and are revalidated. If your host supports response-header configuration, Cache-Control: no-cache requires successful validation before a stored response is reused; changing a local file alone does not configure that header. RFC 9111: HTTP Caching.
Keep one stable sitemap address
Make the public sitemap discoverable by submitting its stable URL through Search Console’s Sitemaps report or referencing it in the site’s existing robots.txt file. Google describes both methods and notes that submission is a hint. Build and submit a sitemap.
For the illustrative site, the discovery line would be:
Sitemap: https://example.com/sitemap.txt
Keep the sitemap address stable while replacing its contents during publication. The practical completion check is that the public sitemap matches the finished export, the added page opens at its listed URL, and the removed page has the intended response. Search engines process those changes on their own schedules; sitemap inclusion does not guarantee crawling or indexing. Learn about sitemaps.
Sources
- Build and submit a sitemap — Google Search Central.
- Learn about sitemaps — Google Search Central.
- Sitemaps XML format — Sitemaps.org.
- HTML Standard: Links — WHATWG.
- Using Python on macOS — Python Software Foundation.
- RFC 9110: HTTP Semantics — RFC Editor / IETF.
- RFC 9111: HTTP Caching — RFC Editor / IETF.
- How HTTP status codes affect Google's crawlers — Google Crawling Infrastructure.