Last active
July 3, 2025 05:29
-
-
Save ArtMin96/5ae6f6c5a3752e2482ed63f6da0dd42c to your computer and use it in GitHub Desktop.
Ngrok with HTTPS on Laravel
This file contains hidden or 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 | |
$middleware->use([ | |
\App\Http\Middleware\HandleNgrokRequests::class, | |
]); |
This file contains hidden or 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 | |
public function boot(): void | |
{ | |
URL::forceHttps(); | |
URL::useOrigin('https://' . request()->header('X-Forwarded-Host')); | |
} |
This file contains hidden or 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 | |
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); | |
} | |
} | |
} |
This file contains hidden or 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
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