Laravel CORS issue for public files

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Solving the Laravel CORS Issue for Public File Access

Dealing with Cross-Origin Resource Sharing (CORS) errors is a common hurdle in modern full-stack development, especially when mixing frontend frameworks like Angular with backend systems like Laravel. When API calls work perfectly but direct file access fails with a No 'Access-Control-Allow-Origin' header error, it signals that the mechanism governing file delivery is separate from your standard API routing and CORS configuration.

As a senior developer, I can tell you that this issue usually stems from how the web server (or Laravel itself) handles serving static assets versus handling dynamic API responses. Let's break down why this happens and how to correctly configure your Laravel application to allow Angular to access public files securely.

Understanding the CORS Discrepancy

You are encountering a classic CORS error because the browser enforces strict security policies. When the Angular application tries to fetch https://myurl.com/storage/rmlogs/MyFileNameDetail.log, it treats this as a request for a resource that must explicitly permit cross-origin access via HTTP headers.

Your current setup—with custom cors.php and middleware—is expertly configured to handle requests coming from your API endpoints (routes defined in /api/*). However, direct file access is often handled by the underlying web server configuration (like Nginx or Apache) or Laravel's default public file serving mechanism, which might bypass or ignore your custom CORS middleware if it's not explicitly applied to that specific route type.

The fact that you can access the file via Postman or a direct browser request confirms the file exists and is accessible by the server; the issue lies purely in the browser's security context regarding cross-origin requests.

The Laravel Solution: Serving Files Securely

In a robust Laravel application, we should avoid exposing raw file paths directly unless absolutely necessary. Instead, we leverage Laravel’s Storage facade to serve files securely through controlled routes, which allows us to apply our CORS policies consistently.

Step 1: Use Controller Methods for File Delivery

Instead of letting the client request a direct file path, create an API endpoint that handles the file retrieval. This ensures that every file access is authenticated and subjected to your middleware rules.

First, ensure your files are correctly linked in your public directory (e.g., by using php artisan storage:link).

Next, define a route and a controller method to handle the download or streaming of the log file.

// routes/api.php

use App\Http\Controllers\LogController;

Route::get('/logs/{filename}', [LogController::class, 'downloadFile'])
    ->middleware('cors'); // Apply the CORS middleware here!

Step 2: Implement the File Download Logic

In your controller, you will use Laravel's Storage facade to read the file contents and stream them back to the client with the correct headers. This is much safer than linking a public URL directly, as it keeps access governed by PHP logic.

// app/Http/Controllers/LogController.php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Storage;

class LogController extends Controller
{
    public function downloadFile($filename)
    {
        // Ensure the file exists and is accessible in storage
        $path = "rmlogs/{$filename}.log"; // Adjust path based on your actual structure
        
        if (!Storage::disk('public')->exists($path)) {
            abort(404, 'File not found.');
        }

        // Get the file contents
        $fileContents = Storage::disk('public')->get($path);

        // Return the file content with appropriate headers for download
        return response($fileContents, 200, [
            'Content-Type' => 'text/plain', // Or appropriate mime type
            'Content-Disposition' => 'attachment; filename="' . $filename . '.log"',
        ]);
    }
}

Step 3: Reviewing Your CORS Configuration

While the file delivery mechanism is key, let's re-examine your custom configuration. The goal of having allowed_origins set to * and allowed_headers set to * in cors.php is generally fine for development or public APIs. However, when dealing with file streams, you must ensure that the response headers are explicitly set within your controller method (as shown above) rather than relying solely on the middleware's default behavior for static requests.

For more advanced configuration and understanding how Laravel manages resource access, diving into official documentation is always recommended. For comprehensive guidance on building secure and scalable applications with Laravel, I highly recommend exploring resources from laravelcompany.com.

Conclusion

The CORS issue you faced was a symptom of mismatched expectations between dynamic API requests and static file delivery. By shifting the logic to use dedicated, middleware-protected controller routes and leveraging the Storage facade, you gain complete control over access permissions. This pattern ensures that your application adheres to Laravel's principles of security and architecture, providing a stable and secure foundation for both your APIs and public assets.