Skip to content

fix: head requests to /api/media/file/:filename return 404 instead of 200 (RFC 9110 §9.3.2)#17470

Open
Asadullah-shz wants to merge 1 commit into
payloadcms:mainfrom
Asadullah-shz:fix/head-request-media-file-404
Open

fix: head requests to /api/media/file/:filename return 404 instead of 200 (RFC 9110 §9.3.2)#17470
Asadullah-shz wants to merge 1 commit into
payloadcms:mainfrom
Asadullah-shz:fix/head-request-media-file-404

Conversation

@Asadullah-shz

Copy link
Copy Markdown

Problem

Per RFC 9110 §9.3.2, a HEAD response must be identical to GET except for
the absence of a response body. However, HEAD /api/media/file/:filename
returned 404 while GET returned 200 for the exact same URL.

URL='https://your-site.example.com/api/media/file/portrait.jpg'
curl -sI "$URL" | head -n1
# Observed: HTTP/2 404
# Expected: HTTP/2 200
curl -s -o /dev/null -w '%{http_code}\n' "$URL"
# Observed: 200 (GET works fine)
This causes operator scripts and upstream proxies that use HEAD as a cheap existence probe to report false 404s.

**Root Cause**
Two compounding issues:

HEAD was not exported from @payloadcms/next/routes — so Next.js App Router had no explicit HEAD handler, falling back to its auto-synthesis which failed to reach the media file route.

handleEndpoints passed the full body back for HEAD — the HEAD→GET routing fallback correctly found the getFileHandler, but the handler opened an fs.createReadStream and returned a Response with a full body stream. The body was passed through unchanged, leaking an open file handle and producing a non-RFC-compliant response.

**Fix**
packages/next/src/routes/rest/index.ts
Export HEAD = handlerBuilder so Next.js App Router registers an explicit HEAD handler at the route level.

packages/next/src/routes/index.ts + packages/next/src/exports/routes.ts
Re-export HEAD as REST_HEAD so consumers can import it explicitly in their app/(payload)/api/[...slug]/route.ts.

packages/payload/src/utilities/handleEndpoints.ts
When HEAD is resolved via the GET fallback:

Cancel the body stream immediately — releases the fs.createReadStream file handle, prevents resource leaks.
Return new Response(null, ...) — body-less, but with identical Content-Type, Content-Length, Accept-Ranges, and status code, fully compliant with RFC 9110 §9.3.2.
test/__helpers/shared/NextRESTClient.ts
Add a HEAD() method wired to REST_HEAD so integration tests can issue HEAD requests end-to-end.

test/uploads/int.spec.ts
Add regression test verifying:

HEAD /media/file/:filename → 200
content-type header matches (image/png)
Uploaded document is cleaned up after the test

@Asadullah-shz Asadullah-shz changed the title fix: HEAD /api/media/file/:filename returns 404 instead of 200 (RFC 9110 §9.3.2) fix: head requests to /api/media/file/:filename return 404 instead of 200 (RFC 9110 §9.3.2) Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant