Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jkbicierro's avatar

[SOLVED] Why do images (e.g., PNG, JPEG) retrieved from protected storage appear broken or fail to load properly, and how can this issue be resolved?

Controller:

public function preview($id) 
{
    $attachment = HR_Attachments::findOrFail($id);

    $file_path = $attachment->file_path;

    if (!Storage::disk('protected')->exists($file_path)) {
        abort(404, 'File not found.');
    }

    $absolutePath = Storage::disk('protected')->path($file_path);
    $mimeType = Storage::disk('protected')->mimeType($file_path);

    return response()->file($absolutePath, [
        'Content-Type' => $mimeType,
        'Cache-Control' => 'private, no-store, no-cache',
    ]);
}

Route:

Route::get('/attachments/{id}/preview', [AttachmentController::class, 'preview'])->name('attachments.preview');

Blade:

@if (in_array(strtolower($attachment->file_type), ['png', 'jpg', 'jpeg']))
    <img
        src="{{ route('attachments.preview', $attachment->id) }}"
        alt="{{ $attachment->title ?? 'Attachment' }}"
        class="w-full rounded-xl border object-cover"
    />
@else
    <iframe
        src="{{ route('attachments.preview', $attachment->id) }}#toolbar=0"
        class="w-full h-[600px] border"
    ></iframe>
@endif
1 like
3 replies
jlrdw's avatar

Any log errors? What does the network tab show?

1 like
jkbicierro's avatar

name: preview, status: 200 type: document, no errors

Network:

HTTP/1.1 200 OK
Host: localhost:8000
Connection: close
X-Powered-By: PHP/8.1.25
Content-Type: image/jpeg
Cache-Control: no-cache, no-store, public
Date: Sun, 28 Sep 2025 07:54:06 GMT
Last-Modified: Sun, 28 Sep 2025 07:06:28 GMT
Content-Length: 8044
Accept-Ranges: bytes
1 like
Snapey's avatar

one possible cause is accidentally sending one or two characters to the browser before the file content. eg having a file with the opening php tag on line 2, or closing a .php file with ?> and a new line.

Examine the first few hex bytes of the file response with the file on disk. You can do this in most editors with a hex plugin

Please or to participate in this conversation.