Why Headings Deserve IDs
An element with an ID becomes linkable through standard browser behavior. That's valuable for headings, since being able to point someone straight to a specific part of a page is often exactly what you want — either from a URL or from an on-page link inside the content.
So the ideal end state is simple: every heading gets a unique ID, and the trouble is only in getting there. Writing those IDs by hand, post after post, is tedious. A common workaround has been to add them (along with a self-link # inside the heading) via jQuery on the client side. That markup gives readers a visual cue that the heading is linkable and makes right-click copy-link easy. One such implementation ran on this site for years.
The Race You Don't Want to Win
Then it breaks — not the script, but the native browser behavior it was leaning on. When the page loads with a fragment in the URL, the browser looks for that ID very early in the rendering process. If the ID doesn't exist yet, the browser moves on. When the script runs a beat later and injects the IDs, the window for that initial jump has already closed.
It's a race condition between page render and script execution, and blaming the browser for not chasing dynamically inserted anchors isn't fair. It's surprising the approach stayed reliable for as long as it did.
Better: Fix It Before the Browser Sees It
The robust fix is to have the IDs already present in the HTML when it arrives. On a WordPress site, that naturally points to a content filter. As it happens, a purpose-built plugin — Karolína Vyskočilová's Add Anchor Links — handles this neatly: rather than mutating the heading tag itself, it puts the ID on the anchor link inside it. That sidesteps any risk of colliding with an ID you might have set yourself.
Without WordPress, the same principle applies: process the HTML server-side, whatever that takes. A service worker is also a valid spot for this kind of rewriting, and Cloudflare's HTMLRewriter is one streaming option well suited to the task.



