ScreenshotNeo

BlogHow-to

How to Fix PNG Images That Don’t Display in HTML

Fix broken PNGs in HTML by checking the requested URL, deployed file, MIME type, image data, and conditional CORS errors.

By the ScreenshotNeo team30 September 20269 min read

How to Fix PNG Images That Don’t Display in HTML

A PNG that does not appear in HTML usually fails for one of five reasons: the browser requested the wrong URL, the file was not deployed there, the server returned the wrong response type, the image data is invalid, or a conditional cross-origin rule blocked the request. Start with the browser’s actual failed request, then follow the evidence in order.

The smallest valid example is:

<img src="images/example.png" alt="Describe the image's meaning">

Replace the illustrative path with the URL where your deployed site really serves the file. The alt value helps people understand a meaningful image when it cannot load, but it does not repair a failed request. For decorative images, use an empty alt="". See MDN’s guidance for the img element and HTML images.

1. Inspect the failed request first

Open Developer Tools in the browser and reload the page with the Network panel visible. Filter by “Img” (or search for .png). Select the failed request and record:

Trace a broken image from the requested URL through the response headers to the PNG bytes.
Trace a broken image from the requested URL through the response headers to the PNG bytes.
  • The complete requested URL, including its origin, path, query string, and filename case.
  • The HTTP status, such as 200, 301, 403, 404, or 500.
  • The response Content-Type header.
  • Whether the response body is actually an image or an HTML error page.
  • Any console message about decoding, CORS, mixed content, or permissions.

The img element loads the resource selected by src or, when responsive markup is used, by srcset. A syntactically valid element can still point to a missing or unusable resource. MDN lists empty sources, corrupt image data, bad metadata, and unsupported formats among possible image loading failures: MDN img reference.

2. Verify the URL and deployed file

Relative paths resolve from the document URL

A relative URL is resolved from the page’s address, not from your source-code editor or project root. If the page is https://example.com/docs/page.html, then:

Compare the URL in your HTML with the file that was actually deployed.
Compare the URL in your HTML with the file that was actually deployed.
<img src="images/example.png" alt="Example chart">

requests https://example.com/docs/images/example.png. If the file is actually at /images/example.png, use an absolute path from the site root:

<img src="/images/example.png" alt="Example chart">

For a different host, use the complete URL:

<img src="https://cdn.example.com/assets/example.png" alt="Example chart">

Check spelling, case, and deployment

Many production servers are case-sensitive. Example.png, example.png, and example.PNG may be different files. Confirm the directory name, filename, extension, and URL encoding. A local path such as C:\Users\Sam\Pictures\example.png or /Users/sam/project/example.png is not a public URL; visitors cannot read your computer’s filesystem.

  1. Copy the URL shown in the Network panel.
  2. Open it in a new tab or request it with curl -I.
  3. Compare that exact path with the files included in the deployed build or object-storage bucket.
  4. Deploy the missing asset, or change src/srcset to the deployed location.
curl -I https://example.com/images/example.png

A 404 confirms a location or deployment problem. A redirect may be valid, but inspect the final response too. A 403 means the file exists but access is denied, often by storage permissions, an application rule, or a hotlink policy.

3. Confirm the server sends image/png

The extension in a URL does not determine how the browser interprets the response. The server should send the PNG media type:

Content-Type: image/png

Check response headers in Developer Tools or with:

curl -I https://example.com/images/example.png

For a valid image, the status should normally be successful and the content type should be image/png. A response such as text/html often means a framework returned an error document, login page, or single-page-app fallback at the image URL. The browser may then report that it cannot decode the image.

Configure the header in the layer that serves the asset: your web server, CDN, object-storage metadata, or framework static-file middleware. MDN explains how MIME types guide processing and identifies PNG as image/png: MIME types and image types.

4. Validate the PNG data

If the URL returns 200 and the media type is correct, inspect the response body. A damaged file, truncated upload, or incorrect conversion can still fail to render. Download the exact response rather than checking only the copy in your working directory:

curl -L https://example.com/images/example.png -o downloaded.png

Open downloaded.png with an image viewer or an image-validation tool. If it cannot be opened locally, regenerate or re-export the PNG from the original source and upload it again. Also check that your build pipeline did not replace the binary with text, apply an unintended transform, or cut the file during transfer.

Do not “fix” a broken PNG by changing only the filename extension. The bytes must contain valid PNG data, and the response must advertise the matching type.

5. Check markup that changes which image loads

Responsive images can fail even when the fallback src is correct. Inspect every candidate in srcset and the sizes rule that determines which candidate the browser chooses:

<img
  src="/images/chart-800.png"
  srcset="/images/chart-400.png 400w, /images/chart-800.png 800w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt="Monthly signups"
>

Test at the viewport widths and device pixel ratios where the problem occurs. A missing high-density candidate can make the image appear broken on one device but not another. Also inspect dynamically assigned src values: JavaScript may run before the asset path is available, produce an empty string, or construct a URL with an unescaped character.

6. Treat CORS as a conditional diagnosis

CORS is not the default explanation for every broken image. It matters when the markup requests cross-origin behavior, especially when crossorigin is present, or when script code needs to read the image (for example, drawing it to a canvas). MDN explains that crossorigin causes a CORS request and that the browser blocks it when the image server does not allow the requesting origin: img element reference.

<img
  src="https://cdn.example.com/photo.png"
  crossorigin="anonymous"
  alt="Product photo"
>

If the console reports a CORS failure, configure the image server to return an Access-Control-Allow-Origin value that permits your site, or remove crossorigin when you do not need a CORS-enabled request. Check the response in Network tools; do not add a permissive header blindly without confirming the request is cross-origin and requires it.

7. A repeatable repair checklist

  1. Reload with DevTools open and identify the exact image request.
  2. Open the requested URL directly and record its status and final URL.
  3. Compare the URL with the deployed filename, directory, spelling, and case.
  4. Verify the response is the PNG bytes, not an HTML or JSON error body.
  5. Verify Content-Type: image/png.
  6. Download the response and validate that the file opens as a PNG.
  7. Inspect srcset, sizes, and JavaScript-generated URLs.
  8. Investigate CORS only when the console and request show a cross-origin restriction.
  9. Reload without cache and test the production URL from a clean browser session.

8. Troubleshooting common errors

Symptom Likely cause Fix
404 Not Found Wrong path, filename, case, or missing deployment Use the exact deployed URL or include the asset in the build.
403 Forbidden Storage, CDN, authentication, or hotlink rule Allow the page’s request to read the object and check signed-URL expiry.
200 but image icon is broken HTML/JSON returned, wrong MIME type, or invalid bytes Inspect the body, set image/png, and re-export the file.
“Failed to decode image” Corrupt, truncated, or non-PNG data Download the response and regenerate the image from a known-good source.
Works locally, fails after deploy Different root path, case-sensitive host, or asset omitted from build Test the production URL shown in Network tools and inspect build output.
Only some viewport sizes fail A selected srcset candidate is missing Request each candidate directly and correct the responsive markup.
Console mentions CORS Cross-origin request with missing permission Configure the image server’s CORS response or remove unnecessary crossorigin.
Mixed-content warning HTTPS page requests an HTTP image Serve the image over HTTPS and update the URL.

9. Performance and reliability considerations

Once the image loads, keep the request dependable. Serve appropriately sized PNGs rather than sending a very large source for a small slot. Use width and height attributes (or CSS aspect-ratio) to reserve layout space while the request completes. Set cache headers at the CDN or static host, and use a versioned filename when the bytes change so clients can safely cache immutable assets.

PNG is useful for lossless graphics, transparency, diagrams, and screenshots. For photographs, another format may be smaller, but changing formats does not fix a wrong URL or invalid response. Keep the original PNG available when transparency or lossless detail is required.

For critical images, monitor the production URL and status, and make deployment checks verify that referenced assets exist. If a CDN transforms images, confirm it preserves a valid content type and complete bytes. During incident diagnosis, compare a direct origin response with the CDN response to find where the data changes.

Or skip the browser setup

If you need reliable screenshots of a page to document or debug its rendered output, ScreenshotNeo provides a single GET request that returns PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the ScreenshotNeo API documentation for all options. The basic calls are:

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

You can request full-page captures with lazy images loaded, a single element by CSS selector, dark mode, 12 device presets or any viewport, retina scale, custom CSS and JavaScript, clicks, selector or network-idle waits, blocked ads/trackers/resources, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage data, and PDF paper, margin, landscape, and page-range settings. Parameter names used by other screenshot APIs also work to ease migration. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Plans include 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Does adding alt make a PNG load?

No. It provides a text alternative for accessibility and fallback communication. Repair the URL, response, file data, or applicable CORS configuration.

Is a 200 status proof that the image is valid?

No. A server can return an HTML error page with status 200. Inspect the body and confirm Content-Type: image/png.

Should I always add crossorigin?

No. Add it only when your use case needs a CORS-enabled request, such as reading the image from script. Otherwise it can introduce an unnecessary CORS requirement.

Why does the same image work in a local development server?

Production may use a different base path, case-sensitive filesystem, CDN rule, or build output. Diagnose the exact production request rather than assuming local paths carry over.

Can a browser display a PNG served with another MIME type?

Do not rely on that behavior. Configure the server to send the correct image/png type so processing is predictable across browsers and intermediaries.