Last active
July 23, 2022 13:59
-
-
Save iksaku/5efaede73f694942f3ea22af75a18c41 to your computer and use it in GitHub Desktop.
Pretender Middleware for Laravel apps that rely heavily on JS frameworks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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