Skip to content

Get started

Most read

Indexability

Can Google still crawl and index your site after this deploy? robots.txt, noindex, canonical, sitemap and share cards, checked on every release.

Updated on 25 September 2026

On this page

The outage this check catches has no symptom elsewhere. A staging robots.txt shipped over the real one, a noindex forgotten on the home page after a redesign, a canonical still pointing to localhost — every URL answers 200, uptime is perfect, and organic traffic dies 2 days later. By then, the responsible deploy is 20 deploys back. It's a site check: created by PostShip, outside the quota, run on every deploy.

What PostShip checks

2 requests, in parallel: robots.txt at the root, and the home page. Everything is a document read — no crawler, no Search Console API, nothing that needs a browser.

  1. robots.txt in server error. A 5xx isn't “no robots.txt”: engines read it as a temporary block of the whole site, which is worse than a 404. A 404 is normal and allows everything.
  2. Disallow: / for everyone. The file is read group by group, not by text search: Disallow: / only means “block everything” in a User-agent: * group, and a Disallow: /admin in the same file is normal. A group that also carries Allow: / isn't a block — both rules have the same length and the protocol rules in favour of Allow. A group targeting a named robot isn't a site block.
  3. The X-Robots-Tag header of the home page: noindex or none removes the page from the index. It weighs as much as the tag and is set by accident more easily, from a proxy or a framework configuration.
  4. The meta name="robots" tag: same 2 directives. nofollow, noarchive and the others change how the page is processed, not whether it's listed — they aren't failures.
  5. The canonical. A rel="canonical" to another domain — localhost, a staging domain, a competitor — is a failure. A canonical between www.acme.fr and acme.fr is noted but never fails: many sites deliberately canonicalise one way or the other.

Set it up

Nothing to create: the target exists from the project's creation, on the origin. It's read on Project → Monitoring → Health, “Indexability” card, which shows the verdict in plain words — “Waiting for the first deploy” as long as no ship has been seen. The detail (robots.txt status, directives read, canonical) is on the target's page.

The check only runs on deploy, at T+2 / T+8 and on “Check now”: see Site checks for the why. It can be turned off in Project → URLs, where each has its row, but it's rarely the right choice — a site you don't want indexed says so with noindex, and that's precisely what you want to know.

What triggers an alert

CodeScreen sentenceAlert sentence
robots_disallow_allThe robots.txt forbids crawling the whole site (Disallow: /).The robots.txt of https://acme.fr/ forbids crawling the whole site.
robots_5xxThe robots.txt returns a server error: engines read that as a block of the whole site.The robots.txt of https://acme.fr/ returns a server error.
meta_noindexThe home page carries a meta robots noindex tag.https://acme.fr/ asks search engines not to index it (noindex).
x_robots_tag_noindexThe X-Robots-Tag header returns noindex.Same sentence.
canonical_foreign_hostThe canonical link points to a domain other than yours.The canonical of https://acme.fr/ points to another domain.

The alert never says “answers 200”: the whole point of the check is that the page looks fine. A non-indexable site costs 30 points on the Ship Score — right behind a payment page, because the cost arrives weeks later, in traffic. On Pro Plus, that's enough to fail the GitHub Check if the minimum score is set.

Limits and plans

DiscoverProPro Plus
Check run and verdict on HealthYesYesYes
Alert sentNoYesYes
Place in the quota000

On Discover, the verdict is computed, stored and shown; it's the notification the plan doesn't carry.

Troubleshooting

Why is my staging site failing?

Because it carries a noindex or a Disallow: /, and that's exactly what it must carry. Don't monitor a staging as a project: use the previews to check a deploy before production.

The robots.txt blocks a single robot, why no alert?

A User-agent: GPTBot group with Disallow: / only blocks that robot: it isn't a site block. For answer engines' robots, see AI visibility, which asks that other question.

Why isn't the canonical to www. flagged?

Because it's almost always deliberate. The www-variant verdict is written in the run's detail, without failure. What is checked here is the domain; the canonical's form (absolute, http or https) belongs to the tags.

Open Graph

Check that a page's social card stays presentable — title, description, an image that loads.

When a link to your site is pasted in Slack, on LinkedIn or on X, it's the Open Graph card that shows: a title, a description, an image. A redesign that loses the og:image tag, an image moved by a deploy, a description emptied by a template — nothing is visible on the site, and every share becomes a bare link. The OG / Twitter target reads these tags and goes to check that the image responds.

What PostShip checks

PostShip requests the page (same rules as the HTTP check: 12 seconds, 5 redirects, 512 KB) and reads 4 tags, written as meta property="…" or meta name="…":

TagRule
og:titlePresent, and 70 characters at most — beyond that, the networks cut it.
og:descriptionPresent.
og:imagePresent.
twitter:cardPresent (summary or summary_large_image).

Then, if the image is on your site (same domain or subdomain), a HEAD on its address:

  • it must answer 200;
  • its Content-Type must be image/jpeg, image/png, image/webp or image/gif;
  • its Content-Length, if announced, must stay under 8 MB — the practical ceiling the networks' robots apply.

An image hosted elsewhere (a CDN, an image service) is not requested: serving your images from a CDN is the norm, and hitting any host written in a tag would make PostShip a request relay. Its presence is checked, not its response.

What isn't checked: the visual rendering of the card. It's a check of tags and of the image's reachability, not a screenshot. To see the card as it is, and what it was before a deploy, there is the ship archive.

Set it up

Project → URLs → “Monitor…” → “A page” → “Its social card”. The URL is the one of the page whose link is shared — the home page, an article, a product page. There is no other setting: the thresholds are the networks', not yours.

Add one OG target per template (the home page, an article, a product sheet) rather than one per page: the tags come from the template, and if one breaks, all break.

What triggers an alert

CodeSentence
og:titleThe title (og:title) is missing.
og:title_too_longThe title (og:title) exceeds 70 characters.
og:descriptionThe description (og:description) is missing.
og:imageThe share image (og:image) is missing.
og:image reachableThe share image (og:image) is not reachable.
og:image_typeThe share image format is not supported.
og:image_too_heavyThe share image exceeds 8 MB.
twitter:cardThe twitter:card tag is missing.

All these gaps put the target in failure. When the image is missing or doesn't respond, the alert says: The social card no longer has a valid image. A broken OG card costs 15 points on the Ship Score. The alert respects the project's confirmation and goes out once per outage.

Limits and plans

The check is the same on all plans and counts as one URL of the quota (Discover 5, Pro 25, Pro Plus 100). The social card archive — the before / after — keeps 30 days on Pro, 90 on Pro Plus.

Troubleshooting

Why is the image declared unreachable when it displays?

The HEAD answered something other than 200: often a 403 from an anti-bot protection or from a bucket that refuses HEAD, sometimes a 301 to a CDN (the status compared is the one of the response at the tag's address, after following redirects). Check with curl -I the exact address written in og:image.

Why is the image type not supported?

The server announces a Content-Type outside the 4 accepted — image/svg+xml, application/octet-stream, or text/html because the address returns a page. Social networks have the same requirement: serve JPEG, PNG, WebP or GIF with the right type.

The tags are there in my browser but missing for PostShip

They are injected by JavaScript. Social networks' robots don't run your JavaScript either: put the tags in the served HTML.

Sitemap and SSL

The sitemap is read and its pages probed; the certificate is followed to its expiry, step by step.

2 checks that have nothing to do with each other, except that they concern the whole site rather than a page. The sitemap tells engines what exists: an empty or truncated sitemap takes pages out of the index without any page breaking. The certificate tells browsers you can come in: an expired certificate closes the site with a red warning, on a date everybody knew.

Sitemap

What PostShip checks

PostShip requests the sitemap's URL (12 seconds, 5 redirects, 512 KB read) and extracts the <loc>s. If it's a sitemap index (<sitemapindex>), it follows up to 3 child sitemaps — on your domain only — and takes their pages. Then:

  1. There must be URLs. Zero URLs is an empty sitemap, or not a sitemap.
  2. A sample of 10 URLs of your domain receives a HEAD. A URL that answers 400 or more, or doesn't respond, is unreachable.
  3. What disappeared. The URL list is memorised (up to 2,000) and compared with that of a run at least one day old. A disappearance is a gap when it's too wide to be an editorial removal: at least 3 pages AND at least 5% of the previous sitemap. Below that — an unpublished article — it's a warning, because it's almost always intended.
  4. Do the departed pages still respond? Up to 10 of the disappeared URLs receive a HEAD. A page that redirects, anywhere, or still responds is in order; a page in 404 or 410 without redirect is a loss: engines lose the page and the links that pointed to it.

Why one day of hindsight: comparing with the last run flagged a disappearance a single time — the next run compared it with a list that no longer contained it. With one day, a removal is flagged for 24 hours, then becomes the norm.

Set it up

Project → URLs → “Monitor…” → “A page” → “A sitemap”, with the sitemap's URL (https://acme.fr/sitemap.xml). A new project receives it automatically when your answers at sign-up say that SEO matters.

What triggers an alert

CodeSentenceEffect
no_urls_foundThe sitemap contains no URL — or it isn't a sitemap.Failure
sampled_urls_unreachableSome sitemap URLs don't respond: engines find errors there.Failure
pages_disparuesSome pages disappeared from the sitemap since the previous run — a redesign that forgot some?Failure
pages_perdues_sans_redirectionSome pages removed from the sitemap answer 404 instead of redirecting: engines and inbound links lose them.Failure
pages_retireesA few pages were removed from the sitemap.Warning, without alert

The target's detail lists up to 20 disappeared URLs and those lost without redirect. An entirely emptied sitemap is no_urls_found: it isn't flagged twice.

SSL

What PostShip checks

PostShip opens a TLS connection on port 443 (or 8443 if the URL specifies it; no other port is monitored) and reads the end date of the presented certificate. It deduces the number of days left and applies 3 steps: 30 days, 7 days, 1 day, then expired.

Each step is its own code. That's what makes a certificate at “under 30 days” alert once when crossing the step, not on every cycle for 3 weeks: the run's fingerprint changes at each step, and only there.

The certificate checked is the URL's host name's: add one target per host that matters (acme.fr, app.acme.fr). To be told when someone issues a certificate for your domain — another question — see Certificates.

Set it up

Project → URLs → “Monitor…” → “A page” → “Its certificate”. A new project always receives this target on its origin. The project's Health page shows the “SSL certificate” card with the end date and the renewals strip.

What triggers an alert

CodeSentence
ssl_expiring_30dThe SSL certificate expires in less than 30 days.
ssl_expiring_7dThe SSL certificate expires in less than 7 days.
ssl_expiring_1dThe SSL certificate expires in less than 24h.
ssl_expiredThe SSL certificate has expired.

Errors (no verdict): “TLS timeout (12s).”, “Certificate not found.”, “Port not monitored.”. A certificate under 14 days costs 10 points on the Ship Score, even if the SSL target is still green.

Limits and plans

DiscoverProPro Plus
Cycle5 min2 min1 min
Child sitemaps followed333
URLs probed per run101010
URLs memorised for the diff2,0002,0002,000

The Pro Plus 30-second probe only watches pages: the sitemap and the certificate follow the cycle.

Troubleshooting

Why does the sitemap alert at 30 days when Let's Encrypt renews at 30 days?

It isn't the sitemap but the certificate, and it's intended: an automatic renewal that works resets the counter to 90 days before the next step. If you then receive the 7-day alert, the renewal didn't happen — that's exactly the moment to act.

Why “some pages disappeared” when I removed nothing?

The sitemap is probably generated on the fly and answered truncated or partial on one run (512 KB limit, error page, index whose child didn't respond). The run's detail gives the number of URLs read; compare it with your real sitemap.

Why is the certificate “not found”?

The server didn't present a certificate for that name: a host missing from the proxy, or a CDN that hasn't issued the certificate yet. Test with openssl s_client -servername acme.fr -connect acme.fr:443.

SEO tags

Title, meta description, canonical, viewport and hreflang of the home page, as an engine reads them.

An engine only has the served HTML to decide what to show in its results: the <title> becomes the blue link, the description the grey text under it, the canonical designates the URL that gets the credit, the viewport says whether the page deserves the mobile index. A redesign that loses the description in a layout, a template that leaves “Home” as title, a relative canonical written by hand: nothing visible to a human, but positions lost in a few weeks. It's a site check: created by PostShip, outside the quota, run on deploy.

What PostShip checks

A read of the home page's HTML, without a browser. This check looks only at what no other judges: indexability already settles the noindex, the robots.txt and a canonical to another host; the Open Graph card already judges og:title, og:description and og:image. Re-judging here would fail 2 checks for a single defect and double the alerts.

TagRuleWhy these figures
<title>Present; 65 characters at most; at least 15; not a generic title (“Home”, “Accueil”, “Untitled”, “Document”, “New page”, “Index”)Google truncates around 60-70 characters depending on glyph width; under 15, it's usually a brand name alone. A site whose home page is called “Home” never received a title.
meta name="description"Present; 160 characters at most; at least 50Cut around 155-160 on desktop; under 50, Google judges it too poor and replaces it with an excerpt chosen without you. Only name="description" counts — og:description serves the social card.
link rel="canonical"Present; absolute URL in http or httpsA relative path is resolved differently depending on the URL that served the page (with or without slash, with or without www) — the ambiguity the canonical is supposed to lift. The targeted domain is judged by indexability.
meta name="viewport"PresentWithout it, the page isn't mobile-friendly, and the index is mobile first.
hreflangIf present: an x-default, or an entry for the page's language (lang="fr" and hreflang="fr-FR" are consistent)Google requires each page to cite itself in its alternates, otherwise it ignores the whole group.

Whitespace is collapsed before measuring: a <title> indented over 3 lines by a template doesn't count its line breaks, and “Home” surrounded by spaces remains “Home”. The <title> read is the document's, not an inline SVG logo's.

A score out of 100: 20 points per gap, 5 per warning.

Set it up

Nothing to create. Project → URLs, “Tags” row. The target's detail gives the title and its length, the description and its length, the canonical, the presence of the viewport, the hreflangs read, and the score.

The fixes are in your template's <head>. For a canonical, write the full URL (https://acme.fr/), with the trailing slash if that's the one serving the page.

What triggers an alert

Gaps — the target fails:

CodeSentence
titre_absentThe page has no <title>: engines show the raw URL.
titre_trop_longThe <title> exceeds 65 characters: it will be cut in the results.
description_absenteNo meta description: engines make up the excerpt.
viewport_absentNo viewport tag: the page isn't mobile-friendly.
canonical_absentNo canonical tag: engines may index duplicates.
canonical_invalideThe canonical tag isn't an absolute http(s) URL.

Warnings — noted, without alert:

CodeSentence
titre_trop_courtThe <title> is under 15 characters: too vague for a search result.
titre_generiqueThe <title> is generic (“Accueil”, “Home”): it doesn't say what the site is.
description_trop_longueThe meta description exceeds 160 characters: it will be cut.
description_trop_courteThe meta description is under 50 characters: the excerpt will be thin.
hreflang_incoherentThe hreflang tags don't match the page's language.

The alert speaks of the deploy that introduced the defect: The home page lost its meta description — https://acme.fr/, The home page lost its <title> — https://acme.fr/. A failure costs at most 10 points on the Ship Score, “site hygiene” category.

Limits and plans

DiscoverProPro Plus
Check and verdict on HealthYesYesYes
Alert sentNoYesYes

Only the home page is analysed. To monitor the title and description of other pages from one deploy to the next, what changed compares these fields on every HTTP target.

Troubleshooting

My title is 62 characters and Google cuts it anyway

Google cuts in pixels, not in characters: capitals and wide letters fit less. 65 is the middle of the zone; aim for 55 if your title is in capitals.

Why does a canonical to www.acme.fr pass when the site is on acme.fr?

This check only judges the form (absolute, http(s)). The targeted domain is the question of indexability, which notes the www variant without failing, because it's almost always deliberate.

The hreflangs are correct but flagged inconsistent

Check the lang attribute of the html tag: without it, no label can match the page's language. Add lang, or an hreflang="x-default".

Site checks

The 8 checks PostShip sets itself on your domain, outside the quota, and which only run on deploy.

Some questions aren't about a page but about the site: is it indexable, are its security headers there, do its emails arrive, does www. lead somewhere? PostShip asks them itself, once per project, without you having to add anything. Those are the site checks: they don't count in your quota, they only run on deploy, and each one can be turned off on its own.

The 8 checks

CheckQuestion askedPage
IndexabilityCan Google still crawl and index the site?Indexability
SecurityAre the protection headers served?Security headers
RedirectsDo http, www. and the apex lead to the https site?Redirects
CookiesAre trackers set before consent?Cookies and consent
TagsAre title, description, canonical, viewport correct?SEO tags
Exposed filesAre .env, .git, backups readable from the web?Exposed files
LeaksLorem ipsum, staging address, secret key in the HTML?Leaks
Third-party scriptsDid a third-party script change at its host?Third-party scripts

All are set on the project's origin (https://acme.fr/).

How they run

On deploy only. What robots.txt allows, a site's headers, cookies, redirects only change at release. Asking for them again every 5 minutes would redo the same request for the same response. The ordinary cycle therefore leaves them out; only a deploy webhook, a T+2 / T+8 re-check or a “Check now” trigger them. One exception: third-party scripts also run once a day outside a deploy, because it's precisely a change without a deploy they look for.

The consequence to know: without a connected host, a site check has no automatic trigger, and the Checks page shows “not measured yet” until the first ship. Connect Vercel, Netlify, Cloudflare or a generic webhook, or let the detection without webhook recognise your deploys.

Outside the quota. You didn't ask for them; billing them would amount to shrinking your plan without saying so. A Discover project with 5 URLs keeps its 5 URLs.

Gap and warning. Each check distinguishes what puts the target in failure (a gap) from what it notes without alerting (a warning), and computes a score out of 100. Each check's page lists both.

Where to read them

Project → Monitoring → Security: the score out of 100 and what to fix, check by check. Indexability has its card on the Health page. Each check has one of these states:

StateMeaning
Nothing to reportThe last run recorded no gap.
To reviewAt least one gap.
The check couldn't runPostShip couldn't conclude (timeout, DNS).
not measured yetNo deploy checked since creation.

What triggers an alert

A gap puts the check in failure and opens an incident, with the first cause named: The http address no longer redirects to https — https://acme.fr/, The domain no longer has an SPF record — acme.fr. The exact codes are on each check's page. The alert follows the project's confirmation — counted in deploys, since that's where they run — and “Recovered” follows the return to green.

On the Ship Score, site checks form a single category, “site hygiene”, capped at 10 points: 4 missing hygienes must not cost 40 points to a site that sells very well. Indexability (30 points) and AI visibility (25) have their own category.

Limits and plans

DiscoverProPro Plus
Checks created and computedYesYesYes
Verdict visible in Checks and the OverviewYesYesYes
Alerts (email, Discord, Slack)NoYesYes
Place in the URL quota000

On Discover, the checks run and show — it's the notification the plan doesn't carry, not the check. Running a target again by hand doesn't send the alert the plan excludes either.

Troubleshooting

Why is everything “not measured yet”?

No deploy was seen. Click “Check now” in the project bar for a first run, then connect your host for the following ones.

Why does a check go into error on every deploy?

An error is an “I couldn't conclude”: the site took more than 12 seconds, or a third-party directory didn't answer. The target's detail gives the reason. An unavailability that is ours or a third party's is postponed to the next run, never turned into an incident on your side.

Can I add a check on a subdomain?

No: they're set once per project, on its origin. Create one project per subdomain that deserves its own checks.