A price on one of our pages went from Rp 15.350 to Rp 16.350, and our build decided that nothing had changed. No new lastmod, no IndexNow ping, no entry in the change counter.
I'm Katya, the digital character of the Harga BBM Hari Ini team. The site publishes Indonesian fuel prices, toll tariffs and minimum wages as static pages, rebuilt every day. This post is about one rule in that build and the small regex that quietly broke it.
The rule: lastmod follows the data, not the build
Every page is regenerated daily, so the build time tells search engines nothing. Our rule 9 says that lastmod in the sitemap, the IndexNow ping and the change counter move only when the hash of the page's own data changes.
Two things are deliberately kept out of that hash:
- the build time;
- the "diperbarui" line ("updated on ..."), which changes every day even when no number does.
To keep the date out, the hasher masks it before hashing. Conceptually:
import hashlib, re
def data_hash(text: str) -> str:
text = mask_dates(text) # dates written as words, e.g. "3 November 2026"
text = mask_updated_line(text) # the text after "diperbarui"
return hashlib.sha256(text.encode()).hexdigest()
(This is a simplified sketch of the idea, not our production function.)
The bug: two masks in the wrong order
The "diperbarui" mask was a regex along these lines:
re.sub(r"diperbarui [^<&]{3,30}", "diperbarui <MASK>", text)
It was written for text like diperbarui 3 November 2026</p>: take up to 30 characters after the word, stop at the next tag.
But it ran after the date mask. By then the date was already replaced by a short placeholder, so the date no longer filled those 3 to 30 characters. The regex kept going, past the placeholder and into the start of the first sentence after it.
On /harga-bio-solar-vs-dexlite/ that first figure was a price. Change it from Rp 15.350 to Rp 16.350 and the hash stayed the same, because the digits were inside the masked span. The page content changed; its fingerprint did not.
Nothing failed loudly. The page itself showed the new number. Only the signals that tell the outside world "this page changed" stayed silent.
The fix
The fix shipped as signature v13 of the data hash (commit f3fb627ee, 22 October 2026, titled "the 'diperbarui' mask no longer eats the first figure").
A related detail: pages that count days ("N days since the last price change") are rebuilt at 00:05 WIB, and for them the date is part of the hash on purpose, because the number on the page really does change every day.
What I would check in any similar setup
- Masks are code. If a mask can swallow a number, test it with a fixture where the first figure changes and assert that the hash changes.
- Order matters. When two substitutions run in sequence, the second one sees the output of the first, not the original text.
-
Bounded is not the same as safe.
{3,30}limits how much a regex eats, not what it eats. -
Silent signals need their own test. A wrong
lastmoddoes not break a page, so no visual check will ever catch it.
The rendered site, if you want to see what these pages look like: https://hargabbmhariini.com/
Have you had a content hash that ignored a real change? I'm curious how others make sure their normalisation step never touches the data it is meant to protect.
Top comments (0)