How to remove Cache-control header no-cache

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

How to Eliminate Unwanted Cache-Control Headers in Your Laravel API Responses

Dealing with HTTP caching headers is a common frustration for developers building modern APIs. When you are working with complex frontend setups like Laravel APIs communicating with Vue/Apollo clients, managing how browsers and client libraries cache responses becomes critical for performance and data freshness.

My team recently encountered an issue where our Laravel API responses were including aggressive Cache-Control: no-cache, private headers. This immediately caused problems for our Apollo client, which couldn't effectively cache GraphQL results, leading to unnecessary network requests. We dug into PHP and Nginx configurations, but the header seemed to be originating from a layer we weren't expecting.

This post dives deep into diagnosing where these unwanted caching directives originate and provides practical solutions to ensure your Laravel API serves clean, cacheable responses.

Understanding the Root Cause: Where Do Cache Headers Come From?

HTTP headers like Cache-Control are directives sent by the server to instruct caches (proxies, browsers, CDNs) how they should handle the response. If these headers are being added automatically, it is usually due to a framework setting, middleware configuration, or an upstream web server directive.

In our specific scenario involving Laravel and GraphQL, the header was present on the response payload: Cache-Control: no-cache, private. This suggests that some part of the request lifecycle—either within the Laravel application logic or during the response serialization—is explicitly setting this value.

We need to systematically check the layers: PHP (Laravel framework), the specific package used for GraphQL, and the web server (Nginx).

Investigating the Source in the Laravel/PHP Layer

Since we confirmed that standard php.ini settings or simple Nginx add_header directives were insufficient, the most likely culprit resides within the application code or a service layer handling the response generation.

When working with sophisticated packages like folkloreatelier/laravel-graphql, these packages often inject custom headers based on security or privacy requirements. The issue is not necessarily that PHP or Nginx caused the header, but rather that the Laravel application logic intended to apply it during serialization.

Solution 1: Controlling Response Headers in Laravel

The best practice when dealing with API responses in Laravel is to control headers explicitly within your controller methods or using Response objects, ensuring you only send the necessary information. We can intercept and modify the response before it leaves the application layer.

For a clean setup, ensure that any custom header logic is applied conditionally. If you are using Laravel's built-in response handling, you can manually manage headers:

use Illuminate\Http\Response;

class GraphQLController extends Controller
{
    public function handleQuery(Request $request)
    {
        // ... logic to fetch data ...

        $response = response()->json([
            'data' => $fetchedData
        ]);

        // Explicitly remove or overwrite unwanted headers if they are present
        $response->headers->set('Cache-Control', 'public, max-age=3600');
        $response->headers->set('Pragma', 'public'); // Good practice for backward compatibility

        return $response;
    }
}

By manually setting the headers on the Response object, you gain granular control and bypass any potentially problematic defaults set by underlying packages. This approach aligns perfectly with the principles of building robust APIs, much like those promoted by laravelcompany.com.

Addressing Server-Level Configuration (Nginx)

While we found that Nginx configuration was not the primary source, it remains crucial for setting global defaults. If you are using Nginx as a reverse proxy, ensure your proxy settings do not inadvertently strip or modify headers sent by PHP-FPM.

If you still wish to enforce caching rules at the web server level (for general static assets or public endpoints), use directives carefully:

location / {
    # Set default caching for responses from the application
    add_header Cache-Control "public, must-revalidate";
    # Prevent browser caching of specific non-cacheable assets
    expires off; 
}

Conclusion and Best Practices

The mystery of rogue Cache-Control headers often leads us to look at multiple layers. In the case of a Laravel/GraphQL setup, the solution almost always lies in controlling the response generation within the application layer itself, rather than relying solely on server configuration files.

By explicitly managing headers using Laravel's Response objects, you ensure that your API communicates exactly what you intend it to communicate. Always treat HTTP headers as part of your API contract. Remember, robust backend development requires meticulous attention to detail across all layers—from PHP and Nginx down to the application logic itself. For deeper insights into building scalable backend systems, exploring resources like laravelcompany.com is highly recommended.