My setup:
My problem:
If I vagrant ssh
, and then share domfit.test
I get a random generated ngrok url as you'd expect (http://whatever.ngrok.io), however when I access this URL all my resources / routes are being prefixed with http://domfit.test/
(http://domfit.test/login for instance)
I've tried the following:
php artisan config:clear
php artisan cache:clear
{{ url('login') }}
{{ route('login') }}
My understanding is that url()
should return the actual URL that the browser requested (rather than using APP_URL
) but it always returns domfit.test
.
If I rename my site in Homestead.yaml
(for example to newdomfit.test
) and re-provision then this is the domain that url()
and route()
uses, regardless of my APP_URL
. So the Homestead.yaml
seems to be forcing that domain. Which begs the question - how are you meant to actually use the share functionality?
I'm new to Laravel so I am not sure if all of this is expected behavior and I am misunderstanding something?
I just want my links and resources in templates to work for local (domfit.test
), shared (ngrok
) and eventually production with the same piece of code. My worry is I will have to change all of my route()
or url()
references when I attempt to put this website live.
EDIT BELOW
OK I've just tried again. Changed APP_URL
for ngrok
:
Searched my entire codebase for domfit.test
, and only some random session files seem to have references:
code/domfit/storage/framework/sessions/
APP_NAME=DomFit
APP_VERSION=0.01
APP_ENV=local
APP_KEY=XXXX
APP_DEBUG=true
APP_URL=http://04b7beec.ngrok.io
Then in my Controller I have it doing this for some simple debugging:
echo(url('/login'));
echo(route('login'));
echo($_SERVER['HTTP_HOST']);
echo($_SERVER['HTTP_X_ORIGINAL_HOST']);
If I use the ngrok
URL the output I get is:
http://domfit.test/login
http://domfit.test/login
domfit.test
04b7beec.ngrok.io
I don't understand how $_SERVER['HTTP_HOST']
is returning the wrong url?
It looks like it could be related to this: https://github.com/laravel/valet/issues/342
ANOTHER EDIT
It looks like it has to do with Homestead's share
command:
function share() {
if [[ "$1" ]]
then
ngrok http ${@:2} -host-header="$1" 80
else
echo "Error: missing required parameters."
echo "Usage: "
echo " share domain"
echo "Invocation with extra params passed directly to ngrok"
echo " share domain -region=eu -subdomain=test1234"
fi
}
Which passes the option -host-header
to ngrok
which according to their documentation:
Some application servers like WAMP, MAMP and pow use the Host header for determining which development site to display. For this reason, ngrok can rewrite your requests with a modified Host header. Use the -host-header switch to rewrite incoming HTTP requests.
If I use ngrok
without it, then the website that gets displayed is a different one (because I have multiple sites configured in Homestead) - so I'm still not sure how to get around this. For the time being I could disable the other sites as I'm not actively developing those.
Even though you're going to the ngrok url, the host header in the request is still set as the name of your site. Laravel uses the host header to build the absolute url for links, assets, etc. ngrok includes the ngrok url in the X-Original-Host
header, but Laravel doesn't know anything about that.
There are two basic solutions to the issue:
forceRootUrl()
method to ignore the server and header values.TrustedProxies and Forwarded Host
If you're using TrustedProxies (default in Laravel >= 5.5), and you have it configured to trust all proxies (protected $proxies = '*';
), you can set the X-Forwarded-Host
header to the X-Original-Host
header. Laravel will then use the value in the X-Forwarded-Host
header to build all absolute urls.
You can do this at the web server level. For example, if you're using apache, you can add this to your public/.htaccess
file:
# Handle ngrok X-Original-Host Header
RewriteCond %{HTTP:X-Original-Host} \.ngrok\.io$ [NC]
RewriteRule .* - [E=HTTP_X_FORWARDED_HOST:%{HTTP:X-Original-Host}]
If you prefer to handle this in your application instead of the web server, you will need to update the Laravel request. There are plenty of places you could choose to do this, but one example would be in your AppServiceProvider::boot()
method:
public function boot(\Illuminate\Http\Request $request)
{
if ($request->server->has('HTTP_X_ORIGINAL_HOST')) {
$request->server->set('HTTP_X_FORWARDED_HOST', $request->server->get('HTTP_X_ORIGINAL_HOST'));
$request->headers->set('X_FORWARDED_HOST', $request->server->get('HTTP_X_ORIGINAL_HOST'));
}
}
Not Using TrustedProxies
If you're not using TrustedProxies, you can't use the .htaccess
method. However, you can still update the server and headers values in your application. In this case, you'd need to overwrite the Host header:
public function boot(\Illuminate\Http\Request $request)
{
if ($request->server->has('HTTP_X_ORIGINAL_HOST')) {
$request->server->set('HTTP_HOST', $request->server->get('HTTP_X_ORIGINAL_HOST'));
$request->headers->set('HOST', $request->server->get('HTTP_X_ORIGINAL_HOST'));
}
}
Using forceRootUrl()
If you don't want to modify any headers or the Laravel request, you can simply tell the URL generator what root url to use. The URL generator has a forceRootUrl()
method that you can use to tell it to use a specific value instead of looking at the request. Again, in your AppServiceProvider::boot()
method:
public function boot(\Illuminate\Http\Request $request)
{
if ($request->server->has('HTTP_X_ORIGINAL_HOST')) {
$this->app['url']->forceRootUrl($request->server->get('HTTP_X_FORWARDED_PROTO').'://'.$request->server->get('HTTP_X_ORIGINAL_HOST'));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With