Search code examples
htmlimageweb-standards

Why do image and picture elements display images despite http status 404?


The following request returns an HTTP status code of 404, but also content-type: image/jpeg and valid JPEG data in the response body:

https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg

If you set this as the src of an image, the image will still be displayed. Furthermore, an onerror event is not triggered, so I can't use onerror="this.src=this.src.replace('maxres', 'sd')" to trigger a fallback.

This behaviour is consistent across Firefox/Chrome/Edge, so I wonder 1) if this is something defined in the standards? Or is it actually a bug that exists in all major browsers?

Using it as a source in a picture element will also not have it fall back to the next available image size and display the "broken" image as well.

<h3>Plain image element:</h3>
<img src="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg" onerror="this.src=this.src.replace('maxres', 'sd')">

<h3>Picture element:</h3>
<picture>
  <source media="(max-width: 640px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg">
  <source media="(max-width: 480px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/hqdefault.jpg">
  <source media="(max-width: 320px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/mqdefault.jpg">
  <source srcset="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg">
  <img src="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg" type="image/jpeg">
</picture>

<h3>expected output:</h3>
<img src="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg">

2) Is there a way to circumvent this and fall back to the next smaller image size that doesn't result in a 404 status, preferrably without using JavaScript?


Solution

  • Thanks to @Tyler Roper for his comment:

    The documentation states that onerror is thrown when the image "fails to load". In your case, the image has loaded successfully; it's just accompanied by a 404 notifying you that it is not the image that you originally requested.

    For my specific case, I solved it this way:

    <img
      src="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg"
      onload="if (this.naturalWidth < 1280) { this.src = this.src.replace('maxres', 'sd') }">

    So, it loads the maxresdefault first, which should normally be at least 1280px wide. If it's less, the image source is changed to sddefault.