Skip to content

Invalidate your CDN

If you render job pages behind a CDN or a page cache, the Publish API sends no Cache-Control for you to lean on (Caching). Invalidate on webhooks instead.

Every event names the job and the index pages that list it. Purge all of them, every time:

From the payload URL to purge
job.slug {jobs_base_path}/{job.slug}
always {jobs_base_path} (the index of all jobs)
job.service_type, if present category_index_paths.by_service with {slug} replaced
job.service_city, if present category_index_paths.by_location with {slug} replaced
each entry in job.sibling_slugs, if present {jobs_base_path}/{sibling}
each entry in job.sibling_categories, if present the same two index patterns, with that entry’s slugs
always, if you serve one your sitemap

jobs_base_path and category_index_paths come from the Business descriptor; cache that once, it rarely changes.

Treat the three events the same way. On job.unpublished the job will answer 404 from the API on the next fetch, so your origin should start serving its own not-found page for that URL; purging guarantees the cached copy does not outlive it.

  1. Verify the signature (guide). Reject with 401 on failure.
  2. Check delivery.id against the IDs you have already handled. If seen, return 200 and stop.
  3. Record delivery.id.
  4. Queue the purge and return 200 immediately. Do not call your CDN inside the request; the delivery times out at 10 seconds and a slow purge would be counted as a failure and retried.
  5. In the queued job, build the URL list above and call your CDN’s purge API. If the purge fails, retry it on your side; do not rely on SimplifyLocal’s retry, which only fires when you answered non-2xx.
// Node, after verification and dedupe
function urlsToPurge(business, payload) {
const base = `${business.canonical_domain}${business.jobs_base_path}`;
const byService = (s) => `${business.canonical_domain}${business.category_index_paths.by_service.replace("{slug}", s)}`;
const byCity = (c) => `${business.canonical_domain}${business.category_index_paths.by_location.replace("{slug}", c)}`;
const { job } = payload;
const urls = new Set([base, `${base}/${job.slug}`]);
if (job.service_type) urls.add(byService(job.service_type));
if (job.service_city) urls.add(byCity(job.service_city));
for (const s of job.sibling_slugs ?? []) urls.add(`${base}/${s}`);
for (const c of job.sibling_categories ?? []) {
if (c.service_type) urls.add(byService(c.service_type));
if (c.service_city) urls.add(byCity(c.service_city));
}
return [...urls];
}

After the purge, fetch the job with Get a job and render it, or let the next visitor’s request do it. Either way, store the response’s ETag so later renders can revalidate with If-None-Match.

Deliveries are retried for up to about four days, but an endpoint that fails 50 times in a row is disabled. As a backstop, run a periodic sync with List jobs and updated_since, and purge anything whose updated_at moved.