Skip to content

Instantly share code, notes, and snippets.

@ArtMin96
Last active July 3, 2025 05:29
Show Gist options
  • Save ArtMin96/5ae6f6c5a3752e2482ed63f6da0dd42c to your computer and use it in GitHub Desktop.
Save ArtMin96/5ae6f6c5a3752e2482ed63f6da0dd42c to your computer and use it in GitHub Desktop.
Ngrok with HTTPS on Laravel
<?php
$middleware->use([
\App\Http\Middleware\HandleNgrokRequests::class,
]);
<?php
public function boot(): void
{
URL::forceHttps();
URL::useOrigin('https://' . request()->header('X-Forwarded-Host'));
}
<?php
declare(strict_types=1);
use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;
class HandleNgrokRequests
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$ngrokHost = $request->header('X-Forwarded-Host');
if ($ngrokHost && (str_ends_with($ngrokHost, 'ngrok-free.app') || str_ends_with($ngrokHost, 'ngrok.io'))) {
if ($response instanceof RedirectResponse) {
$this->fixRedirectTarget($response, $ngrokHost);
} elseif ($response->isSuccessful()) {
$contentType = $response->headers->get('Content-Type', '');
$isHtml = str_contains($contentType, 'html');
$isInertiaJson = $request->header('X-Inertia') && str_contains($contentType, 'json');
if ($isHtml || $isInertiaJson) {
$this->fixResponseBody($response, $ngrokHost);
}
}
}
return $response;
}
/**
* Fixes a redirect URL by rebuilding it.
*/
private function fixRedirectTarget(RedirectResponse $response, string $ngrokHost): void
{
$targetUrl = $response->getTargetUrl();
$localDomain = config('app.domain');
if (! empty($localDomain) && str_contains($targetUrl, $localDomain)) {
$path = parse_url($targetUrl, PHP_URL_PATH) ?? '/';
$query = parse_url($targetUrl, PHP_URL_QUERY);
$newTarget = 'https://'.$ngrokHost.$path;
if ($query) {
$newTarget .= '?'.$query;
}
$response->setTargetUrl($newTarget);
}
}
/**
* Replaces all occurrences of the local domain in the response body (HTML or JSON).
*/
private function fixResponseBody(Response $response, string $ngrokHost): void
{
$localDomain = config('app.domain');
if (empty($localDomain)) {
return;
}
$content = $response->getContent();
if (is_string($content) && str_contains($content, $localDomain)) {
$escapedDomain = preg_quote($localDomain, '/');
$pattern = '/(https?:\/\/)([\w.-]+\.'.$escapedDomain.')/';
$newContent = preg_replace($pattern, 'https://'.$ngrokHost, $content);
$response->setContent($newContent);
}
}
}
ngrok http --host-header=rewrite subdomain.domain.test:80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment