24

Ok, so for development purposes, we have a dedicated web server. It's not currently connected directly to the internet, so I've setup an apache reverse proxy on another server, which forwards to the development server.

This way, I can get web access to the server.

The problem is, the routes in Laravel are now being prefixed with the internal server IP address, or the servers computer name.

For example, I go to http://subdomain.test.com but all the routes, generated using the route() helper, are displaying the following url: http://10.47.32.22 and not http://subdomain.test.com.

The reverse proxy is setup as such:

<VirtualHost *:80>
    ServerName igateway.somedomain.com

    ProxyRequests Off
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>

    ProxyPass / http://10.47.32.22:80/
    ProxyPassReverse / http://10.47.32.22:80/
    <Location />
        Order allow,deny
        Allow from all
    </Location>
</VirtualHost>

I have set the actual domain name in config\app.php.

Question

How can I set the default URL to use in routing? I don't want it using the internal addresses, because that defeats the point of the reverse proxy.

I've tried enclosing all my routes in a Route::group(['domain' ... group, which doesn't work either.

Phil Cross
  • 9,017
  • 12
  • 50
  • 84

10 Answers10

32

I ran into the same (or similar problem), when a Laravel 5 application was not aware of being behind an SSL load-balancer.

I have the following design:

  • client talks to an SSL load balancer over HTTPS
  • SSL load balancer talks to a back-end server over HTTP

That, however, causes all the URLs in the HTML code to be generated with http:// schema.

The following is a quick'n'dirty workaround to make this work, including the schema (http vs. https):

Place the following code on top of app/Http/routes.php

In latest version of laravel, use web/routes.php

$proxy_url    = getenv('PROXY_URL');
$proxy_schema = getenv('PROXY_SCHEMA');

if (!empty($proxy_url)) {
   URL::forceRootUrl($proxy_url);
}

if (!empty($proxy_schema)) {
   URL::forceSchema($proxy_schema);
}

then add the following line into .env file:

PROXY_URL = http://igateway.somedomain.com

If you also need to change schema in the generated HTML code from http:// to https://, just add the following line as well:

PROXY_SCHEMA = https

In latest version of laravel forceSchema method name has changed to forceScheme and the code above should look like this:

if (!empty($proxy_schema)) {
    URL::forceScheme($proxy_schema);
}
apis17
  • 2,825
  • 2
  • 23
  • 23
TimeLord
  • 436
  • 4
  • 4
13

Ok, so I got it. Hopefully this will help someone in the future.

It seems like Laravel ignores the url property in the config\app.php file for http requests (it does state it's only for artisan), and it instead uses either HTTP_HOST or SERVER_NAME provided by apache to generate the domain for URLs.

To override this default url, go to your routes.php file and use the following method:

URL::forceRootUrl('http://subdomain.newurl.com');

This will then force the URL generator to use the new url instead of the HTTP_HOST or SERVER_NAME value.

Phil Cross
  • 9,017
  • 12
  • 50
  • 84
9

Go to app/Http/Middleware/TrustProxies.php and change the protected variable $proxies like this:

protected $proxies = ['127.0.0.1'];

Just this! Be happy!

Daniel Sá
  • 156
  • 1
  • 2
6

Because laravel route is created not from the config/app itself rather than from the server. My solution is adding the proxy_set_header Host to the nginx's config.

server {
    listen 80;
    server_name my.domain.com;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host  my.domain.com;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:8000;
    } 
}

i'm using laravel 8 with nginx docker inside a running nginx on the machine, so yeah it's double nginx

InOut
  • 61
  • 2
  • 2
  • After I set the Host header proxied to my docker container it finally worked. So there was no need to change any file in the project, but server config – fluid undefined Dec 18 '22 at 06:48
4

add this code in App\Providers\AppServiceProvider class

if (Str::contains(Config::get('app.url'), 'https://')) {
    URL::forceScheme('https');
}
2

Seems the Laravel have more convinient solution.. Check answer there: How do I configure SSL with Laravel 5 behind a load balancer (ssl_termination)?

Community
  • 1
  • 1
Alex
  • 1,297
  • 1
  • 16
  • 12
0

Following up @TimeLord's solution:

In latest version of laravel the name for forced schema has changed and now it is: URL::forceScheme()

0

I know this topic is old a.f but I've been solving this issue by replacing the following line in my DatabaseSessionHandler.pdf [@Illuminate/Session]:

    protected function ipAddress()
    {
        return $_SERVER['HTTP_X_FORWARDED_FOR'];
//        return $this->container->make('request')->ip();
    }

Of course you need to migrate the sesssion table first and set up the config
(.env Variable SESSION_DRIVER=database)

Fabus
  • 1
  • 2
0

For nginx, you don't need to do anything extra in Laravel. The fix can be done at from nginx config;

server {
    listen 80;
    listen [::]:80 ipv6only=on;

    server_name sub.domain.dev;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host  sub.domain.dev;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:8000;
    }
}
0

Figured out a way that much cleaner and only does exactly what the loadbalancer tells it to, add this function to your RouteServiceProvider

protected function enforceProtocol()
{
    if(request()->server->has('HTTP_X_FORWARDED_PROTO')){
        URL::forceScheme(request()->server()['HTTP_X_FORWARDED_PROTO']);
    }
}

and in the boot section, simple call it like so

public function boot()
{
    $this->enforceProtocol();
    //other stuff
}
Mooseh
  • 21
  • 1