Search code examples
laravelnginxdnsnginx-reverse-proxy

Base URL is non-www which results in routes non-www


I have a Laravel 8 project running on an Nginx Webserver in a Docker container through an Nginx Reverse Proxy.

So DNS -> Reverse Proxy -> Docker Webserver container -> PHP-FPM container

The problem is that when I use Laravel's route() it is missing the www resulting in login not working (as well as assets etc). Because I AM on the www domain (because of Nginx redirect) but the route endpoint is non-www (so a different site, token issue I believe).

In my .env I have APP_URL set which I believe is only for CLI commands and additionally I have ASSET_URL set, which works for the asset() functions. I should not have to use ASSET_URL if the source of the problem, my setup, is correct (which it clearly is not).

I CAN bypass the problem by using URL::forceRootUrl(config('app.url')); in my router, that forces the APP_URL set in my .env to be used in the RouteUrlGenerator (I believe)

Output of \URL::to('/') is https://example.com, so NON-WWW (obv. WITHOUT using URL::forceRootUrl(config('app.url'));) Even though my browser's URL is including www, so https://www.example.com/blabla.

I would say something is wrong in my setup, either my DNS, Nginx Reverse Proxy, or Nginx Webserver configs but I can't figure out what it is... I hope it is just a simple redirect fix somehow. I have also already restarted my servers etc and done a php artisan optimize:clear and also config:cache and config:clear to be sure.

Please find below my configs:

DNS via CloudFlare (proxied):

A      example.com  123.123.123.123  Proxied
A      www          123.123.123.123  Proxied
CNAME  *            example.com      DNS Only

My Nginx Reverse Proxy config:

server {
        listen 80;
        listen [::]:80;

        server_name     example.com www.example.com;

        location / {
                proxy_pass      http://localhost:8012;
                proxy_set_header   Host $host;
                proxy_set_header   X-Real-IP $remote_addr;
                proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header   X-Forwarded-Host $server_name;
        }
}

My Nginx webserver config:

server {
#    listen 80;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

server {
#    listen 80 default_server;
    index index.html index.htm index.php;
    server_name www.example.com;
    root /var/www/public;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9012;
        fastcgi_index index.php;
        include fastcgi_params;
        # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_FILENAME /var/www/example.com/public/$fastcgi_script_name;
        # fastcgi_param SCRIPT_FILENAME $document_root/example.com.au/$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    
   # Support Clean (aka Search Engine Friendly) URLs & enable Gzip compression:
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }

    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
}

My "default" Laravel 8 .htaccess:

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Send Requests To Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

My .env:

APP_NAME=SomeSite.com
APP_ENV=production
APP_KEY=base64:sdljkldasjkasjasdjklaslkasd
APP_DEBUG=true
APP_URL=https://www.example.com#Used for CLI function such as artisan...
#ASSET_URL=https://asseturl.test#Only used in asset()
TRUSTPROXIES=*
...

Solution

  • Solution is moving the 301 redirect from Nginx Webserver to Nginx Reverse Proxy (superior level).

    This is the following part of the configuration file:

    server {
        listen 80;
        server_name example.com;
        return 301 https://www.example.com$request_uri;
    }
    

    Edit: I suspect it's related to the part:

    proxy_set_header   Host $host;