ScreenshotNeo

BlogHow-to

How to Test a Website Preview Image Before Sharing

Check your public Open Graph tags, confirm the image loads for crawlers, and verify the final card in the platform where you will share it.

By the ScreenshotNeo team1 October 20267 min read

How to Test a Website Preview Image Before Sharing

Direct answer: inspect the public page response for its Open Graph title, description, and image URL; open the image URL as a crawler would; render a preview with a checker; then paste the link into the destination platform’s own inspector or an unpublished draft. A checker is a first pass, not a guarantee of the final crop or layout.

When someone shares a URL, the destination service fetches the public page and reads metadata from the response. The visible card is usually a combination of title, description, and image. A CMS preview can differ from the HTML that an unauthenticated crawler receives, so inspect the deployed URL itself.

The image must also be reachable. Login walls, robots or firewall rules, hotlink protection, a non-public image host, and metadata inserted only after client-side JavaScript can all prevent a crawler from seeing the intended asset.

Step 1: Publish a crawler-visible page

  1. Use the exact URL you will share, including its canonical host, path, and query string.
  2. Open it in a private browser window to confirm it does not require a session.
  3. Check that the server response contains the metadata in the initial HTML. Do not rely only on fields shown in your CMS editor.
  4. Confirm the page and image are available over HTTPS and do not depend on a local development hostname.
A preview checker reads the public page and image before the destination platform renders its own card.
A preview checker reads the public page and image before the destination platform renders its own card.

Step 2: Inspect the raw HTML

Start with the response headers and HTML. This catches redirects, authentication, and server-side rendering problems before you use a preview checker.

curl -L -sS https://example.com/article \
  -H 'User-Agent: Mozilla/5.0' \
  | grep -iE '(|og:title|og:description|og:image|twitter:card|twitter:image)'</code></pre>
<p>Look for one intended value for each of these fields:</p>
<ul>
<li><code>og:title</code>: the share title.</li>
<li><code>og:description</code>: supporting text.</li>
<li><code>og:image</code>: an absolute, publicly reachable image URL.</li>
<li><code>og:url</code>: the canonical URL, when your site uses canonical metadata.</li>
<li><code>twitter:card</code> and <code>twitter:image</code>: optional Twitter/X-specific overrides when your sharing plan needs them.</li>
</ul>
<p>Inspect the actual response rather than assuming the CMS values were emitted. If the tags are missing from the first response and appear only after JavaScript runs, a crawler may not see them.</p>
<h3 id="check-the-image-response-separately">Check the image response separately</h3>
<pre><code>curl -I -L https://cdn.example.com/images/share-card.jpg</code></pre>
<p>Confirm that the final response is successful, serves an image content type, and does not redirect to a login page or an HTML error document. Also check that the URL can be fetched without cookies or a browser session.</p>
<h2 id="step-3-render-a-preview-checker">Step 3: Render a preview checker</h2>
<p>Paste the public URL into an Open Graph or social preview checker. Review the fetched title, description, image URL, and rendered card. Treat the result as a metadata and fetchability check: third-party mockups can use a different crop, text wrapping, or card layout from the destination app.</p>
<p>Compare the rendered card with the source image at likely display sizes. Keep the subject visible near the center, check that embedded text remains readable after cropping, and make sure important edges are not carrying essential information. There is no single image dimension that applies to every destination.</p>
<h2 id="step-4-verify-in-the-destination-platform">Step 4: Verify in the destination platform</h2>
<p>Use the platform’s URL inspector when one is available, or paste the URL into an unpublished post or draft and inspect the resulting card. This is the deciding check because it uses the platform’s own crawler, cache, crop, and layout.</p>
<figure class="wp-block-image size-large"><img decoding="async" width="1200" height="900" src="https://screenshotneo.com/blog/wp-content/uploads/test-website-preview-image-before-sharing-2.jpg" alt="Removing overlays before capture makes the page image easier to inspect." class="wp-image-42293" srcset="https://screenshotneo.com/blog/wp-content/uploads/test-website-preview-image-before-sharing-2.jpg 1200w, https://screenshotneo.com/blog/wp-content/uploads/test-website-preview-image-before-sharing-2-300x225.jpg 300w, https://screenshotneo.com/blog/wp-content/uploads/test-website-preview-image-before-sharing-2-1024x768.jpg 1024w, https://screenshotneo.com/blog/wp-content/uploads/test-website-preview-image-before-sharing-2-768x576.jpg 768w" sizes="(max-width: 1200px) 100vw, 1200px" /><figcaption>Removing overlays before capture makes the page image easier to inspect.</figcaption></figure>
<p>For LinkedIn, distinguish an article URL share from a directly uploaded image. LinkedIn documents those as separate flows; the Images API’s supported formats and pixel ceiling apply to API image assets, not as a universal requirement for every organic link preview. See the <a href="https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/images-api?view=li-lms-2024-10">LinkedIn Images API documentation</a> for that specific limit (fewer than 36,152,320 pixels).</p>
<h2 id="step-5-test-the-same-kind-of-share-you-will-publish">Step 5: Test the same kind of share you will publish</h2>
<ul>
<li><strong>URL/article share:</strong> paste the link and inspect the generated card.</li>
<li><strong>Direct image upload:</strong> upload the file itself and check the resulting post; page metadata is not used in the same way.</li>
<li><strong>Private or team-only draft:</strong> use this when the platform’s inspector is unavailable, then discard the draft after checking.</li>
</ul>
<p>Repeat the check after changing the URL, image, title, or description. A platform may retain an older fetch, so a correct page can still produce an old card temporarily.</p>
<h2 id="automate-a-repeatable-check">Automate a repeatable check</h2>
<h3 id="python-fetch-and-validate-the-core-tags">Python: fetch and validate the core tags</h3>
<pre><code>import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

page_url = "https://example.com/article"
r = requests.get(page_url, timeout=20, headers={"User-Agent": "preview-check/1.0"})
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")

def meta(property_name):
    tag = soup.find("meta", attrs={"property": property_name})
    return tag.get("content", "").strip() if tag else ""

title = meta("og:title")
description = meta("og:description")
image = urljoin(page_url, meta("og:image"))
print({"title": title, "description": description, "image": image})
if not image:
    raise SystemExit("Missing og:image")
image_response = requests.get(image, timeout=20, allow_redirects=True,
                              headers={"User-Agent": "preview-check/1.0"})
image_response.raise_for_status()
content_type = image_response.headers.get("content-type", "")
if not content_type.startswith("image/"):
    raise SystemExit(f"Not an image response: {content_type}")</code></pre>
<h3 id="node-js-fetch-and-extract-tags">Node.js: fetch and extract tags</h3>
<pre><code>const pageUrl = 'https://example.com/article';
const res = await fetch(pageUrl, {
  headers: { 'user-agent': 'preview-check/1.0' }
});
if (!res.ok) throw new Error(`Page returned ${res.status}`);
const html = await res.text();
const getMeta = (property) => {
  const escaped = property.replace(':', '\\:');
  const match = html.match(new RegExp(`<meta[^>]+property=["']${escaped}["'][^>]+content=["']([^"']*)`, 'i'));
  return match ? match[1] : '';
};
const image = new URL(getMeta('og:image'), pageUrl).href;
console.log({ title: getMeta('og:title'), description: getMeta('og:description'), image });
const imageRes = await fetch(image, { method: 'HEAD' });
if (!imageRes.ok) throw new Error(`Image returned ${imageRes.status}`);
if (!(imageRes.headers.get('content-type') || '').startsWith('image/')) {
  throw new Error('og:image did not return an image content type');
}</code></pre>
<h2 id="or-skip-the-browser-setup">Or skip the browser setup</h2>
<p><a href="https://screenshotneo.com">ScreenshotNeo</a> can capture the public URL so you can inspect the actual page image in an automated workflow. Its consent step accepts cookie banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the page verdict and billing status in <code>X-Page-Verdict</code> and <code>X-Billed</code> headers. It also provides an MCP server for Claude, Cursor, and other MCP clients, with <code>take_screenshot</code>, <code>get_page_info</code>, and <code>capture_pdf</code> tools.</p>
<p>See the <a href="https://screenshotneo.com/docs/">ScreenshotNeo API documentation</a> for all options.</p>
<pre><code>curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/article -o shot.webp</code></pre>
<pre><code>import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/article"}, timeout=90)
open("shot.webp", "wb").write(r.content)</code></pre>
<pre><code>const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/article' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);</code></pre>
<p>One thousand screenshots a month are free with no card; paid plans start at $5 for 3,000. <a href="https://screenshotneo.com/account/sign-up/">Create a free ScreenshotNeo account</a>.</p>
<h2 id="troubleshooting-checklist">Troubleshooting checklist</h2>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Likely cause</th>
<th>Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td>No image or a blank card</td>
<td>Missing <code>og:image</code>, inaccessible URL, or crawler block</td>
<td>Inspect the raw response, open the absolute image URL without cookies, and review robots, firewall, and authentication rules.</td>
</tr>
<tr>
<td>CMS preview differs from the checker</td>
<td>The editor value was not emitted in public HTML</td>
<td>Check the deployed response and server-side template output.</td>
</tr>
<tr>
<td>Checker is right but the live platform is wrong</td>
<td>Different crop, cache, or crawler behavior</td>
<td>Use the platform inspector or a draft as the final authority.</td>
</tr>
<tr>
<td>Old image after an update</td>
<td>Cached preview data</td>
<td>Re-fetch with the platform’s available inspection tool; cache refresh timing differs by service.</td>
</tr>
<tr>
<td>Image URL redirects to HTML</td>
<td>CDN or access rule sends crawlers to a login/error page</td>
<td>Serve the image directly with a public response and an image content type.</td>
</tr>
<tr>
<td>LinkedIn API upload rejected</td>
<td>Unsupported format or asset exceeds the documented pixel ceiling</td>
<td>Use JPG, GIF, or PNG and stay below 36,152,320 pixels for that API asset path.</td>
</tr>
</tbody>
</table>
<h2 id="performance-reliability-and-cost">Performance, reliability, and cost</h2>
<ul>
<li><strong>Performance:</strong> check the page and image independently; a fast HTML response does not help if the image host is slow or blocked.</li>
<li><strong>Reliability:</strong> run checks from an unauthenticated environment and repeat after deploys that change metadata, CDN rules, or image URLs.</li>
<li><strong>Automation:</strong> cache your own metadata results, but keep a platform-native verification step before a high-visibility post.</li>
<li><strong>Cost:</strong> a preview checker may be enough for occasional manual work. For scheduled screenshots, ScreenshotNeo bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing.</li>
</ul>
<h2 id="final-pre-share-checklist">Final pre-share checklist</h2>
<ul>
<li>Exact public URL tested.</li>
<li><code>og:title</code>, <code>og:description</code>, and <code>og:image</code> present in the initial response.</li>
<li>Image URL returns an image without login or cookies.</li>
<li>Subject and any embedded text survive likely crops.</li>
<li>Target platform inspector or draft matches the intended card.</li>
<li>Share type confirmed: URL card versus direct image upload.</li>
</ul>
<h2 id="faq">FAQ</h2>
<h3 id="can-i-trust-an-open-graph-checker-completely">Can I trust an Open Graph checker completely?</h3>
<p>No. It is useful for tags and fetchability, but the destination platform controls its own crop, cache, and layout.</p>
<h3 id="should-i-use-one-universal-image-size">Should I use one universal image size?</h3>
<p>No universal size is established by the evidence here. Test the real destination card and keep important content away from edges likely to be cropped.</p>
<h3 id="why-does-a-logged-in-browser-show-metadata-that-a-checker-cannot-see">Why does a logged-in browser show metadata that a checker cannot see?</h3>
<p>Your session may unlock the page, while the crawler receives a login page, a blocked response, or HTML without client-rendered metadata.</p>
<h3 id="does-a-linkedin-uploaded-image-follow-the-same-rules-as-a-shared-article-url">Does a LinkedIn uploaded image follow the same rules as a shared article URL?</h3>
<p>No. LinkedIn documents URL/article sharing and direct image uploads as separate flows, with API constraints applying to the upload path.</p>
  <aside class="sn-cta sn-cta-end" aria-label="Try ScreenshotNeo">
    <div>
      <p class="kicker">ScreenshotNeo</p>
      <h2>Skip the browser setup</h2>
      <p>One API call returns a clean screenshot: cookie banners, popups and chat widgets removed before the shot. Bot checks and blank pages are never billed. 1,000 shots a month free.</p>
    </div>
    <div class="row"><a class="btn btn-primary" href="https://screenshotneo.com/account/sign-up/">Get a free key</a><a class="btn btn-line" href="https://screenshotneo.com/docs/">Read the docs</a></div>
  </aside>
</div>
  </div>
</article>
<section class="wrap related" aria-labelledby="rel-h">
  <h2 id="rel-h">Keep reading</h2>
  <div class="posts">    <article class="post-card has-img"><a class="pc-img" href="https://screenshotneo.com/blog/full-page-capture-chrome-plugin/" tabindex="-1" aria-hidden="true"><img width="800" height="450" src="https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin-800x450.jpg" class="attachment-sn-card size-sn-card wp-post-image" alt="Full-Page Capture Chrome Plugin" fetchpriority="low" decoding="async" srcset="https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin-800x450.jpg 800w, https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin-300x169.jpg 300w, https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin-1024x576.jpg 1024w, https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin-768x432.jpg 768w, https://screenshotneo.com/blog/wp-content/uploads/full-page-capture-chrome-plugin.jpg 1200w" sizes="(max-width: 800px) 100vw, 800px" /></a><p class="pc-meta"><span>7 min read</span></p><h2><a href="https://screenshotneo.com/blog/full-page-capture-chrome-plugin/">Full-Page Capture Chrome Plugin</a></h2><p class="pc-ex">Capture an entire webpage in Chrome with extensions, understand their limits, and automate clean full-page screenshots with ScreenshotNeo.</p></article>
      <article class="post-card has-img"><a class="pc-img" href="https://screenshotneo.com/blog/view-webcam-in-browser/" tabindex="-1" aria-hidden="true"><img width="800" height="450" src="https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser-800x450.jpg" class="attachment-sn-card size-sn-card wp-post-image" alt="How to View a Webcam in Your Browser" fetchpriority="low" decoding="async" srcset="https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser-800x450.jpg 800w, https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser-300x169.jpg 300w, https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser-1024x576.jpg 1024w, https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser-768x432.jpg 768w, https://screenshotneo.com/blog/wp-content/uploads/view-webcam-in-browser.jpg 1200w" sizes="(max-width: 800px) 100vw, 800px" /></a><p class="pc-meta"><span>6 min read</span></p><h2><a href="https://screenshotneo.com/blog/view-webcam-in-browser/">How to View a Webcam in Your Browser</a></h2><p class="pc-ex">Allow camera access, choose the right webcam, and fix browser, Windows, macOS, and permission problems with this practical guide.</p></article>
      <article class="post-card has-img"><a class="pc-img" href="https://screenshotneo.com/blog/facebook-open-graph-debugger/" tabindex="-1" aria-hidden="true"><img width="800" height="450" src="https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger-800x450.jpg" class="attachment-sn-card size-sn-card wp-post-image" alt="Facebook Open Graph Debugger" fetchpriority="low" decoding="async" srcset="https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger-800x450.jpg 800w, https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger-300x169.jpg 300w, https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger-1024x576.jpg 1024w, https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger-768x432.jpg 768w, https://screenshotneo.com/blog/wp-content/uploads/facebook-open-graph-debugger.jpg 1200w" sizes="(max-width: 800px) 100vw, 800px" /></a><p class="pc-meta"><span>7 min read</span></p><h2><a href="https://screenshotneo.com/blog/facebook-open-graph-debugger/">Facebook Open Graph Debugger</a></h2><p class="pc-ex">Use Facebook’s Open Graph debugger to refresh stale link previews, verify metadata, and fix incorrect titles, descriptions, and images.</p></article>
  </div>
</section>
</main>
<footer class="foot">
  <div class="wrap foot-in">
    <div><a href="https://screenshotneo.com/" class="brand"><svg width="22" height="22" viewBox="0 0 32 32" aria-hidden="true" style="flex:none"><rect width="32" height="32" rx="8" fill="var(--ink)"/><path d="M8 13V8h5M19 8h5v5M24 19v5h-5M13 24H8v-5" fill="none" stroke="var(--bg)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/><circle cx="16" cy="16" r="3.4" fill="var(--plum)"/></svg><span class="brand-word">Screenshot<b>Neo</b></span></a><p class="mt-s">Clean website screenshots, PDFs and page data from one API.</p></div>
    <nav aria-label="Footer"><a href="https://screenshotneo.com/docs/">API docs</a><a href="https://screenshotneo.com/openapi.json">OpenAPI</a><a href="https://screenshotneo.com/#mcp">MCP server</a><a href="https://screenshotneo.com/pricing/">Pricing</a><a href="https://screenshotneo.com/blog/">Blog</a><a href="https://screenshotneo.com/account/sign-in/">Sign in</a><a href="mailto:hello@screenshotneo.com">hello@screenshotneo.com</a></nav>
    <div class="foot-note"><span>© 2026 Yorker Media · <a href="https://screenshotneo.com/terms/">Terms</a> · <a href="https://screenshotneo.com/privacy/">Privacy</a> · <a href="https://screenshotneo.com/acceptable-use/">Acceptable use</a> · <a href="https://screenshotneo.com/refunds/">Refunds</a></span><span>Real Chrome · a fresh browser for every shot · only clean shots billed</span></div>
  </div>
</footer>
<script type="speculationrules">
{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/blog/*"},{"not":{"href_matches":["/blog/wp-*.php","/blog/wp-admin/*","/blog/wp-content/uploads/*","/blog/wp-content/*","/blog/wp-content/plugins/*","/blog/wp-content/themes/screenshotneo/*","/blog/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
</script>
<script data-wp-strategy="defer" defer id="sn-blog-js" src="https://screenshotneo.com/blog/wp-content/themes/screenshotneo/assets/blog.js?ver=1.0.0.1790644992"></script>
</body>
</html>