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
- What PostShip checks
- Set it up
- What triggers an alert
- Limits and plans
- Troubleshooting
- Why is my staging site failing?
- The robots.txt blocks a single robot, why no alert?
- Why isn't the canonical to www. flagged?
- Open Graph
- What PostShip checks
- Set it up
- What triggers an alert
- Limits and plans
- Troubleshooting
- Sitemap and SSL
- Sitemap
- SSL
- Limits and plans
- Troubleshooting
- SEO tags
- What PostShip checks
- Set it up
- What triggers an alert
- Limits and plans
- Troubleshooting
- Site checks
- The 8 checks
- How they run
- Where to read them
- What triggers an alert
- Limits and plans
- Troubleshooting
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.
robots.txtin 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.Disallow: /for everyone. The file is read group by group, not by text search:Disallow: /only means “block everything” in aUser-agent: *group, and aDisallow: /adminin the same file is normal. A group that also carriesAllow: /isn't a block — both rules have the same length and the protocol rules in favour ofAllow. A group targeting a named robot isn't a site block.- The
X-Robots-Tagheader of the home page:noindexornoneremoves 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. - The
meta name="robots"tag: same 2 directives.nofollow,noarchiveand the others change how the page is processed, not whether it's listed — they aren't failures. - The canonical. A
rel="canonical"to another domain —localhost, a staging domain, a competitor — is a failure. A canonical betweenwww.acme.frandacme.fris 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
| Code | Screen sentence | Alert sentence |
|---|---|---|
robots_disallow_all | The robots.txt forbids crawling the whole site (Disallow: /). | The robots.txt of https://acme.fr/ forbids crawling the whole site. |
robots_5xx | The 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_noindex | The home page carries a meta robots noindex tag. | https://acme.fr/ asks search engines not to index it (noindex). |
x_robots_tag_noindex | The X-Robots-Tag header returns noindex. | Same sentence. |
canonical_foreign_host | The 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
| Discover | Pro | Pro Plus | |
|---|---|---|---|
| Check run and verdict on Health | Yes | Yes | Yes |
| Alert sent | No | Yes | Yes |
| Place in the quota | 0 | 0 | 0 |
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="…":
| Tag | Rule |
|---|---|
og:title | Present, and 70 characters at most — beyond that, the networks cut it. |
og:description | Present. |
og:image | Present. |
twitter:card | Present (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-Typemust beimage/jpeg,image/png,image/webporimage/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
| Code | Sentence |
|---|---|
og:title | The title (og:title) is missing. |
og:title_too_long | The title (og:title) exceeds 70 characters. |
og:description | The description (og:description) is missing. |
og:image | The share image (og:image) is missing. |
og:image reachable | The share image (og:image) is not reachable. |
og:image_type | The share image format is not supported. |
og:image_too_heavy | The share image exceeds 8 MB. |
twitter:card | The 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:
- There must be URLs. Zero URLs is an empty sitemap, or not a sitemap.
- A sample of 10 URLs of your domain receives a
HEAD. A URL that answers 400 or more, or doesn't respond, is unreachable. - 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.
- 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
| Code | Sentence | Effect |
|---|---|---|
no_urls_found | The sitemap contains no URL — or it isn't a sitemap. | Failure |
sampled_urls_unreachable | Some sitemap URLs don't respond: engines find errors there. | Failure |
pages_disparues | Some pages disappeared from the sitemap since the previous run — a redesign that forgot some? | Failure |
pages_perdues_sans_redirection | Some pages removed from the sitemap answer 404 instead of redirecting: engines and inbound links lose them. | Failure |
pages_retirees | A 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
| Code | Sentence |
|---|---|
ssl_expiring_30d | The SSL certificate expires in less than 30 days. |
ssl_expiring_7d | The SSL certificate expires in less than 7 days. |
ssl_expiring_1d | The SSL certificate expires in less than 24h. |
ssl_expired | The 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
| Discover | Pro | Pro Plus | |
|---|---|---|---|
| Cycle | 5 min | 2 min | 1 min |
| Child sitemaps followed | 3 | 3 | 3 |
| URLs probed per run | 10 | 10 | 10 |
| URLs memorised for the diff | 2,000 | 2,000 | 2,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.
| Tag | Rule | Why 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 50 | Cut 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 https | A 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" | Present | Without it, the page isn't mobile-friendly, and the index is mobile first. |
hreflang | If 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:
| Code | Sentence |
|---|---|
titre_absent | The page has no <title>: engines show the raw URL. |
titre_trop_long | The <title> exceeds 65 characters: it will be cut in the results. |
description_absente | No meta description: engines make up the excerpt. |
viewport_absent | No viewport tag: the page isn't mobile-friendly. |
canonical_absent | No canonical tag: engines may index duplicates. |
canonical_invalide | The canonical tag isn't an absolute http(s) URL. |
Warnings — noted, without alert:
| Code | Sentence |
|---|---|
titre_trop_court | The <title> is under 15 characters: too vague for a search result. |
titre_generique | The <title> is generic (“Accueil”, “Home”): it doesn't say what the site is. |
description_trop_longue | The meta description exceeds 160 characters: it will be cut. |
description_trop_courte | The meta description is under 50 characters: the excerpt will be thin. |
hreflang_incoherent | The 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
| Discover | Pro | Pro Plus | |
|---|---|---|---|
| Check and verdict on Health | Yes | Yes | Yes |
| Alert sent | No | Yes | Yes |
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
| Check | Question asked | Page |
|---|---|---|
| Indexability | Can Google still crawl and index the site? | Indexability |
| Security | Are the protection headers served? | Security headers |
| Redirects | Do http, www. and the apex lead to the https site? | Redirects |
| Cookies | Are trackers set before consent? | Cookies and consent |
| Tags | Are title, description, canonical, viewport correct? | SEO tags |
| Exposed files | Are .env, .git, backups readable from the web? | Exposed files |
| Leaks | Lorem ipsum, staging address, secret key in the HTML? | Leaks |
| Third-party scripts | Did 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:
| State | Meaning |
|---|---|
| Nothing to report | The last run recorded no gap. |
| To review | At least one gap. |
| The check couldn't run | PostShip couldn't conclude (timeout, DNS). |
| not measured yet | No 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
| Discover | Pro | Pro Plus | |
|---|---|---|---|
| Checks created and computed | Yes | Yes | Yes |
| Verdict visible in Checks and the Overview | Yes | Yes | Yes |
| Alerts (email, Discord, Slack) | No | Yes | Yes |
| Place in the URL quota | 0 | 0 | 0 |
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.