> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Twitter Card Auditor

> Validates X (Twitter) Card meta tags so shared links render a proper preview

<Info>
  **SEO Auditor** — validates the X (Twitter) Card meta tags so links shared on X render a proper preview.
</Info>

<CardGroup cols={2}>
  <Card title="Social sharing" icon="share-2">
    X builds the card shown under a shared link from the `twitter:*` meta tags of the page.
  </Card>

  <Card title="Click-through" icon="image">
    A card type, title and reachable image make shared links stand out in the timeline and get clicked.
  </Card>
</CardGroup>

The [X (Twitter) Cards markup reference](https://developer.x.com/en/docs/x-for-websites/cards/overview/markup) describes the `<meta name="twitter:…">` tags X reads when a link is posted. This auditor fetches the page, collects every `twitter:` meta tag, verifies the two tags every card needs (`twitter:card` and `twitter:title`), checks the card type and the tags each card type depends on, checks that `twitter:url` and `twitter:image` are absolute and consistent with the page, and probes the `twitter:image` URL once to confirm it serves a supported image type.

## How it works

1. **Fetch** — requests the HTML through the core HTTP fetcher (including HTTP Basic Auth when provided). A blocked request (HTTP 403, 429 or a Cloudflare challenge) ends with `37A-TwitterCardAuditor-590`; any other fetch failure produces an `ERROR` response with `37A-TwitterCardAuditor-500`.
2. **Collect** — reads every `<meta>` whose `name` (or `property`) starts with `twitter:`, keeping document order and repeated values.
3. **Probe** — when `twitter:image` is an absolute URL, sends one `HEAD` request (falling back to `GET` on `405`) and records the HTTP status and `Content-Type`; both image rules read that single answer.
4. **Rules** — a missing or blank `twitter:card` or `twitter:title` is a FAIL. Empty optional tags, duplicated tags, unknown `twitter:` tags, an unsupported card type, relative or off-host URLs, card types without the tags they need, an image without alt text, and an unreachable or non-image `twitter:image` are WARNINGs.
5. **Aggregate** — `ERROR` wins, then `FAIL`, then `WARNING`; if every rule passes, the audit is `SUCCESS`.
6. **Specification pin** — every run emits `37A-TwitterCardAuditor-100` (`INFORMATION`) naming the X Cards markup reference. That check does not change overall status.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`. `37A-TwitterCardAuditor-100` is emitted on every run, including fetch errors. Image rules `312` and `313` only run when `twitter:image` is absolute, and `313` is skipped when `312` fires. Card type comparisons ignore case. CheckCode suffixes use status ranges: `100` information, `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-TwitterCardAuditor-100" />`37A-TwitterCardAuditor-100` | `INFORMATION` | Every run. Records that this auditor implements the X (Twitter) Cards markup reference published at `https://developer.x.com/en/docs/x-for-websites/cards/overview/markup`. | — (recommendation is `null`; nothing for the site to change) | — |
| <a id="37A-TwitterCardAuditor-400" />`37A-TwitterCardAuditor-400` | `FAIL` | A required tag (`twitter:card` or `twitter:title`) is absent from the page. One check per tag. | Add `<meta name="{tag}" content="{example}">` to the `<head>` of `{url}`. | Without twitter:card and twitter:title X has no card to render, so links shared from the page appear as bare URLs and get far fewer clicks. |
| <a id="37A-TwitterCardAuditor-401" />`37A-TwitterCardAuditor-401` | `FAIL` | A required tag is present but every value is blank. One check per tag. | Fill the content attribute of `{tag}` on `{url}`, for example `<meta name="{tag}" content="{example}">`. | An empty card type or title gives X nothing to render, so the shared link shows no preview and the empty tag hides the problem from whoever maintains the page. |
| <a id="37A-TwitterCardAuditor-300" />`37A-TwitterCardAuditor-300` | `WARNING` | An optional (non-required) `twitter:` tag has a blank value. One check per tag. | Fill the content attribute of `{tag}` on `{url}` or remove the tag. | X takes an empty value as the final answer instead of falling back to the page or the Open Graph tags, so the card loses that detail and the shared link gets fewer clicks. |
| <a id="37A-TwitterCardAuditor-301" />`37A-TwitterCardAuditor-301` | `WARNING` | A `twitter:` tag is declared more than once; the message lists the values. One check per tag. | Keep a single `{tag}` on `{url}` (`"{firstValue}"`) and remove the other `{count - 1}`. | When a tag is repeated, X picks one value and other tools pick another, so the card may show a title, image or description you did not intend. |
| <a id="37A-TwitterCardAuditor-302" />`37A-TwitterCardAuditor-302` | `WARNING` | A `twitter:` tag is neither required nor one of the optional tags the markup reference defines. One check per tag. | Remove `{tag}` from `{url}` or rename it to a documented tag such as twitter:description, twitter:image or twitter:site. | X ignores tags it does not know, so the detail the tag was meant to add never reaches the card and the page carries dead markup that hides a typo in a tag that does matter. |
| <a id="37A-TwitterCardAuditor-303" />`37A-TwitterCardAuditor-303` | `WARNING` | `twitter:card` is not `summary`, `summary_large_image`, `app` or `player`. | Set twitter:card on `{url}` to summary, summary\_large\_image, app or player instead of `"{cardType}"`. | X only renders the card types it defines; with an unknown type the shared link shows no preview at all and draws far fewer clicks. |
| <a id="37A-TwitterCardAuditor-304" />`37A-TwitterCardAuditor-304` | `WARNING` | `twitter:image` is present but not an absolute URL. | Set twitter:image on `{url}` to an absolute https URL, for example `"{resolved}"`. | X does not resolve a relative image path, so the card renders without a picture and the shared link stands out far less in the feed. |
| <a id="37A-TwitterCardAuditor-305" />`37A-TwitterCardAuditor-305` | `WARNING` | `twitter:url` is present but not an absolute URL. | Set twitter:url on `{url}` to the canonical absolute URL, for example `"{resolved}"`. | A relative twitter:url cannot be attributed to a page, so the card may link to the wrong address or be dropped, and engagement is split across several URLs. |
| <a id="37A-TwitterCardAuditor-306" />`37A-TwitterCardAuditor-306` | `WARNING` | `twitter:url` is absolute and its host differs from the audited page host (skipped when `305` applies). | Point twitter:url on `{url}` at the canonical URL on `{pageHost}` instead of `"{twitterUrl}"`, or audit `{twitterUrlHost}` instead. | X attributes the card to the twitter:url host, so engagement is credited to another site and the card can send visitors away from your page. |
| <a id="37A-TwitterCardAuditor-307" />`37A-TwitterCardAuditor-307` | `WARNING` | `twitter:card` is `summary_large_image` but `twitter:image` is absent or blank. | Add `<meta name="twitter:image" content="https://…">` pointing at a 2:1 image of at least 300×157 pixels to `{url}`, or change twitter:card from `"{cardType}"` to `"summary"`. | A large-image card exists to show a picture; without one X falls back to a plain summary, so the shared link loses the visual space it asked for and draws fewer clicks. |
| <a id="37A-TwitterCardAuditor-308" />`37A-TwitterCardAuditor-308` | `WARNING` | `twitter:card` is `player` but `twitter:player` is absent or blank. | Add `<meta name="twitter:player" content="https://…">` with the HTTPS URL of the player iframe to `{url}`, or change twitter:card from `"{cardType}"` to `"summary"`. | A player card without a player URL cannot embed the media, so X shows no card and people cannot watch or listen from the timeline. |
| <a id="37A-TwitterCardAuditor-309" />`37A-TwitterCardAuditor-309` | `WARNING` | `twitter:card` is `player` but `twitter:player:width` or `twitter:player:height` is absent or blank; the message names the missing one(s). | Add `{missingTags}` to `{url}` with the player size in pixels, for example `<meta name="twitter:player:width" content="480">` and `<meta name="twitter:player:height" content="270">`. | X needs the player dimensions to reserve space for the embed; without them the card is rejected and the media never plays inline. |
| <a id="37A-TwitterCardAuditor-310" />`37A-TwitterCardAuditor-310` | `WARNING` | `twitter:card` is `app` but none of `twitter:app:name:iphone`, `twitter:app:name:ipad` or `twitter:app:name:googleplay` has a value. | Add at least one app platform to `{url}`, for example `<meta name="twitter:app:name:iphone" content="…">` with its twitter:app:id:iphone, or change twitter:card from `"{cardType}"` to `"summary"`. | An app card without an iPhone, iPad or Google Play app name has nothing to promote, so X shows no install button and the card brings no downloads. |
| <a id="37A-TwitterCardAuditor-311" />`37A-TwitterCardAuditor-311` | `WARNING` | `twitter:image` has a value but `twitter:image:alt` is absent or blank. | Add `<meta name="twitter:image:alt" content="…">` describing `"{image}"` in up to 420 characters to `{url}`. | Without alt text, screen reader users hear nothing about the card image, so part of the audience misses the message and the page falls short of accessibility expectations. |
| <a id="37A-TwitterCardAuditor-312" />`37A-TwitterCardAuditor-312` | `WARNING` | The absolute `twitter:image` answered a non-2xx status or the request failed; the check resource is the image URL. | Make `"{image}"` answer HTTP 200 with a public image (last answer: `{HTTP status or error}`), or point twitter:image on `{url}` at a reachable URL. | When the image cannot be fetched, the card renders without a picture, and shared links without a preview image are clicked far less. |
| <a id="37A-TwitterCardAuditor-313" />`37A-TwitterCardAuditor-313` | `WARNING` | The reachable `twitter:image` is not served as `image/jpeg`, `image/jpg`, `image/png`, `image/gif` or `image/webp` (skipped when `312` applies); the check resource is the image URL. | Serve `"{image}"` as image/jpeg, image/png, image/gif or image/webp instead of `"{contentType or missing}"`. | X ignores an image it cannot decode, so the card renders without a picture and the shared link draws fewer clicks. |
| <a id="37A-TwitterCardAuditor-500" />`37A-TwitterCardAuditor-500` | `ERROR` | The page could not be fetched (network error, timeout, HTTP 404, HTTP 5xx). | Make `{url}` reachable over HTTP(S) and re-run the audit. Last error was `"{error}"`. | — |
| <a id="37A-TwitterCardAuditor-200" />`37A-TwitterCardAuditor-200` | `SUCCESS` | Every rule above passed. Message is `Twitter Card "{cardType}" with title "{title}" is valid on {url}; {foundCount} twitter: tag(s) found, twitter:image served as {contentType}.` | — | — |
| <a id="37A-TwitterCardAuditor-590" />`37A-TwitterCardAuditor-590` | `ERROR` | The audited page answered HTTP 403, HTTP 429 or a Cloudflare challenge, so 37AuditsBot was blocked before it could read the page. | Allow 37AuditsBot in your firewall or bot protection: allowlist its user agent, or verify its Web Bot Auth signature. See [https://www.37audits.com/bot](https://www.37audits.com/bot). | While 37AuditsBot is blocked this audit cannot look at the site, so problems on it go unreported. |

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | `twitter:card` and `twitter:title` are present, the card type and its dependent tags are consistent, `twitter:url` and `twitter:image` are absolute and on the page host, and the image is reachable as a supported type | Both required tags have a non-blank value and no warning rule fires (`200`). |
| **WARNING** | A card will render, but some tags are weak or inconsistent | Empty optional tag (`300`); duplicated tag (`301`); unknown `twitter:` tag (`302`); unsupported card type (`303`); relative `twitter:image` (`304`); relative `twitter:url` (`305`); `twitter:url` on another host (`306`); `summary_large_image` without image (`307`); `player` without player (`308`) or dimensions (`309`); `app` without platform (`310`); image without alt (`311`); image unreachable (`312`); image not an image type (`313`). |
| **FAIL** | A required tag is missing or empty, so X has no card to render | `twitter:card` or `twitter:title` absent (`400`) or present with blank content (`401`). |
| **ERROR** | The auditor could not read the page | Page fetch failed (`500`); 37AuditsBot was blocked (`590`). |
| **INFORMATION** | Specification pin | Always emitted (`100`); does not change overall status. |

## Risks and considerations

### Required tags

* `twitter:card` selects the layout and `twitter:title` is the headline; without either, X shows a bare link. X falls back to `og:title`, `og:description` and `og:image` for the other fields, but not for the card type.
* A required tag with an empty `content` is as bad as a missing one and is harder to spot in a template, so it fails rather than warns.

### Card types

* Only `summary`, `summary_large_image`, `app` and `player` render; `photo`, `gallery` and `product` cards were retired and now produce no preview.
* A `summary_large_image` card needs `twitter:image`; a `player` card needs `twitter:player` with `twitter:player:width` and `twitter:player:height`; an `app` card needs at least one `twitter:app:name:*` platform.
* Comparisons of the card type ignore case, so `Summary` passes `303` but is still worth normalising.

### URLs and the preview image

* `twitter:url` is not part of the current markup reference but is widely used; when present it should be the canonical absolute URL on the page host, otherwise the card is attributed to another address.
* `twitter:image` must be an absolute, publicly fetchable URL served as JPEG, PNG, GIF or WebP (SVG is not rendered). A `403`, `404` or timeout leaves the card without a picture.
* The auditor probes with `HEAD` first and retries with `GET` on `405`; servers that reject `HEAD` with other status codes will warn under `312`.
* `twitter:image:alt` (up to 420 characters) is read by screen readers; it is a warning, not a failure, because the card still renders without it.

### Repeated, empty and unknown tags

* X uses the first occurrence of a repeated tag; repeating `twitter:title` or `twitter:image` is ambiguous and usually a template leftover.
* An optional tag with an empty `content` is treated as the final value, so it should be filled or removed.
* Known tags are the ones listed in the markup reference plus `twitter:url`, `twitter:domain`, `twitter:player:stream:content_type` and `twitter:app:country`; any other `twitter:` tag warns so a typo in a tag that matters (for example `twitter:imag`) is caught.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.