Skip to content

Instantly share code, notes, and snippets.

@murdercode
Last active February 2, 2024 08:02
Show Gist options
  • Save murdercode/cb061b178d2883017a247b1f6c24e345 to your computer and use it in GitHub Desktop.
Save murdercode/cb061b178d2883017a247b1f6c24e345 to your computer and use it in GitHub Desktop.
Optimizing Laravel (and Nova) with FastCGI Cache Configuration

Laravel + FastCGI Cache = ❤️

⚠️ Need a more specific guide? See https://medium.com/@murdercode/speed-up-your-laravel-application-up-to-1000x-with-fastcgi-cache-0135b11407e5

Using FastCGI cache allows you to speed up your website up to 1000x. In fact, the FastCGI cache (or Varnish) mechanism consists of putting a server-caching mechanism between a client and your web server. The whole page will be cached as an HTML output, and it will be delivered instead of using the PHP/MySQL/Redis stack, etc. for all users, but only for the first visit (and others after some specified time).

WARNING: This is not a take-away how-to. Please read it carefully and use it at your own risk.

This config is based on the ploi.io stack. We will not cover the FastCGI installation process, so please prepare FastCGI and adapt the next config if you need it.

What you need to know

To achieve significant improvements in speed and security, it's important to note that pages will be cached and shared among users. Therefore, it's crucial that sessions and cookies aren't cached, as doing so may result in shared sessions and security complications.

This caching method is cookieless and sessionless, and is ideal for websites without a login mechanism. However, it also -supports Laravel Nova authentication.

1. Disable Session & Cookies in Laravel

The first thing to do is to remove the middlewares that create and store sessions and cookies. You can move those middlewares to another group for reuse by modifying the following method in /app/Http/Kernel.php:

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'cookie' => [
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

Please note that VerifyCSRF can pose a security issue if you don't know how to approach it.

2. Define where to use sesssions and cookies

As we mentioned earlier, we want to use sessions and cookies in Laravel Nova (although you can choose to use them elsewhere). So, edit /app/Providers/AppServiceProvider.php as follows:

public function boot()
    {
        if(request()->hasCookie('YOURAPPNAME_session') || request()->is('cms/*') || request()->is('nova-api/*') || request()->is
            ('nova-vendor/*')) {
            $this->app['router']->pushMiddlewareToGroup('web', \App\Http\Middleware\EncryptCookies::class);
            $this->app['router']->pushMiddlewareToGroup('web', \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class);
            $this->app['router']->pushMiddlewareToGroup('web', \Illuminate\Session\Middleware\StartSession::class);
            $this->app['router']->pushMiddlewareToGroup('web', \Illuminate\View\Middleware\ShareErrorsFromSession::class);
            $this->app['router']->pushMiddlewareToGroup('web', \App\Http\Middleware\VerifyCsrfToken::class);
    }

Note: in our case we will use cms as Nova path instead nova. Change it with yours. Note that nova-api can be used from Laravel Nova Tools, so add it as shown.

3. Configure FastCGI

We need to define some conditions, where FastCGI will be not enabled.

In fastcgi-cache.conf:

# Create a variable to skip the cache
set $skip_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $skip_cache 1;
}

# Don't cache when there is a query string (e.g. ?search=query)
#if ($query_string != "") {
#    set $skip_cache 1;
#}

# Don't cache if querystring 
if ($query_string ~* "nocache") {
    set $skip_cache 1;
}
if ($query_string ~* "query") {
    set $skip_cache 1;
}

# Set here your NAME_session, you can take it from HTTP response
if ($http_cookie ~* "YOUR-SESSION-NAME_session") {
    set $skip_cache 1;
}

# Don't cache uris containing the following segments
if ($request_uri ~* "/cms/|/telescope/|/horizon/|/nova-api/|/nova-vendor/|/feed|/.*sitemap.*\.(xml|xsl)") {
    set $skip_cache 1;
}

Now it's time to ignore some headers for preventing misconfiguration.

In fastcgi-php-cache, after add_header add:

fastcgi_cache NAMECACHE;
fastcgi_cache_valid any 5s;

# Allow only 1 request to cache content
fastcgi_cache_lock on;
# Use old cache when it's updating
fastcgi_cache_use_stale updating;
fastcgi_cache_background_update on;

add_header X-FastCGI-Cache $upstream_cache_status;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

#Custom Laravel
fastcgi_cache_key       "$request_method $scheme://$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
#fastcgi_pass_header     Set-Cookie;
fastcgi_pass_header     Cookie;
fastcgi_ignore_headers  Set-Cookie Cache-Control Expires Vary;
fastcgi_hide_header     Expires;
fastcgi_hide_header     Pragma;
fastcgi_hide_header     Vary;

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;

Note that this config will use a microcaching mechanism. Please update with your values if you need it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment