GIF/HTML polyglot test file
A file that is simultaneously a valid GIF (correct GIF89a header and dimensions) and valid HTML containing an inert script. Renders as an image to an image parser and as a page to a browser.
↓ Download polyglot-gif-html.gif| Filename | polyglot-gif-html.gif |
| Format | GIF + HTML polyglot |
| Size | 192 bytes (192 bytes) |
| MIME type | image/gif |
| SHA-256 | c965bb91779eaa8bbf8a03cca8ea28223de282237a38b736541b8f14eb724b5f |
Handle it in code
Each snippet below was run against this exact file with Python 3.14.4 and Node 24.15.0 when this page was built, and the output shown is what it printed.
Python
A valid image can also contain HTML. Browsers that sniff content may render it as a page, running its script on your domain. Serve uploads with their stored Content-Type and X-Content-Type-Options: nosniff, ideally from a separate domain; this check flags files worth a closer look.
MARKERS = (b"<html", b"<script", b"<body", b"<iframe", b"<svg", b"<!doctype html")
with open("polyglot-gif-html.gif", "rb") as f:
head = f.read(1024)
kind = {b"GIF8": "GIF", b"\x89PNG": "PNG", b"\xff\xd8\xff\xe0": "JPEG"}.get(head[:4], "unknown")
found = [m.decode() for m in MARKERS if m in head.lower()]
if found:
print(f"starts as {kind}, but the first 1 KB also contains {', '.join(found)}: a sniffing browser may render it as HTML")
else:
print(f"{kind}: no HTML markers in the first 1 KB")Run against this file, it prints: starts as GIF, but the first 1 KB also contains <html, <script: a sniffing browser may render it as HTML