Skip to content

Instantly share code, notes, and snippets.

@iksaku
Last active July 23, 2022 13:59
Show Gist options
  • Save iksaku/5efaede73f694942f3ea22af75a18c41 to your computer and use it in GitHub Desktop.
Save iksaku/5efaede73f694942f3ea22af75a18c41 to your computer and use it in GitHub Desktop.
Pretender Middleware for Laravel apps that rely heavily on JS frameworks
<?php
namespace App\Http\Middleware;
use Cache;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Psr\Http\Message\ResponseInterface;
class Prerender
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($this->shouldPrerender($request)) {
$urlKey = base64_encode($request->fullUrl());
if (!Cache::tags(['prerender'])->has($urlKey)) {
try {
$response = $this->requestRenderedPage($request->fullUrl());
} catch (GuzzleException $exception) {
logger()->error('Unable to handle Prerender request: '.$exception->getMessage());
return $next($request);
}
logger()->info('Caching '.$request->fullUrl().'...');
Cache::tags(['prerender'])->forever($urlKey, $response->getBody()->getContents());
Cache::tags(['prerender'])->forever($urlKey.':code', $response->getStatusCode());
}
logger()->info('Returning cached content from '.$request->fullUrl());
return response(
Cache::tags(['prerender'])->get($urlKey),
Cache::tags(['prerender'])->get($urlKey.':code', 200)
);
}
return $next($request);
}
/**
* @param Request $request
* @return bool
*/
private function shouldPrerender(Request $request): bool
{
return app()->environment('production')
&& $this->comesFromCrawler($request)
&& $request->isMethod('GET')
&& !$request->header('X-Inertia');
}
/**
* @param Request $request
* @return bool
*/
private function comesFromCrawler(Request $request): bool
{
return !empty($request->userAgent())
&& Str::contains(
strtolower($request->userAgent()),
config('web.crawlers')
);
}
/**
* @param string $url
* @return ResponseInterface
* @throws GuzzleException
*/
private function requestRenderedPage(string $url): ResponseInterface
{
$client = new Client([
RequestOptions::ALLOW_REDIRECTS => false,
RequestOptions::HTTP_ERRORS => false,
]);
return $client->request('GET', 'https://service.prerender.cloud/'.$url);
}
}
<?php
return [
'crawlers' => [
// Search Engines
'googlebot', // Google
'duckduckbot', // DuckDuckGo
'bingbot', // Bing
'yahoo', // Yahoo
'yandexbot', // Yandex
// Social
'facebookexternalhit', // Facebook
'twitterbot', // Twitter
'whatsapp', // WhatsApp
'linkedinbot', // LinkedIn
'slackbot', // Slack
// Others
'ia_archiver', // Alexa
],
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment