skip to Main Content

I have found a possible way how to handle multiple Laravel projects dynamically in one NGINX server (so in one foldder there may be multiple folders for each laravel project. The entry point of each laravel project is /public folder where the index.php is located). The main idea behind this is to easily install the new laravel projects just by adding a new folder with it and be able to access it via my subdomain and path. Like www.webs.example.com/my-new-project

All credit to Henno

This is a NGINX conf for my domain

    # Capture $project from /$projectname/controller/action
map $request_uri $project {

    ~^/(?<captured_project>[a-zA-Z0-9_-]+)/? $captured_project;
    default / ;
}

server {

    listen 443; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/webs.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webs.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    server_name webs.example.com www.webs.example.com;

    # Use $project/public as root
    root /var/www/laravel/$project/public;

    # Use index.php as directory index
    index index.php;

    # Include the basic h5bp config set (see https://github.com/h5bp/server-configs-nginx)
    # include h5bp/basic.conf;

    # Process /projectname/the/rest/of/the/url
    location ~ ^/([^/]+)/(.*) {

        # Save the rest of the URL after project name as $request_url
        set $request_url /$2;


        # If the saved url refers to a file in public folder (a static file), serve it,
        # else redirect to index.php, passing along any ?var=val URL parameters
        try_files $uri $request_url /index.php?$is_args$args;

    }

    # Process any URL containing .php (we arrive here through previous location block)
    # If you don't need to serve any other PHP files besides index.php, use location /index.php here
    # instead, to prevent possible execution of user uploaded PHP code
    location ~ [^/].php(/|$) {

        # Define $fastcgi_script_name and $fastcgi_path_info
        fastcgi_split_path_info ^(.+?.php)(/.*)$;

        # Immediately return 404 when script file does not exist
        if (!-f $document_root$fastcgi_script_name) {
            return 404;
        }

        # Mitigate https://httpoxy.org/ vulnerabilities
        fastcgi_param HTTP_PROXY "";

        # Define PHP backend location (find yours by grepping "listen ="
        # from your PHP config folder, e.g. grep -r "listen =" /etc/php/)
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;

        # Set SCRIPT_FILENAME to execute
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

        # Include the default fastcgi parameters
        include fastcgi_params;

        # Overwrite REQUEST_URI (default is $request_uri) with $request_url we saved earlier
        fastcgi_param  REQUEST_URI        $request_url;
    }

}

This configuration works fine for web entry point

If I access webs.example.com/<project_name> (e.g. webs.example.com/myProject) I get a rendered index page. So request is correctly handled to run /var/www/laravel/<project_name>/public/index.php file.

However, the page is "broken"

And its broken because all resources (like css and js files, images, …) are in folders /public/resources and /public/images and are accessed in code like

<link rel="stylesheet" href="{{ asset('/resources/fontawesome/css/all.css') }}">

<img src="{{ url('/') }}{{$banner->image}}" alt="" class="img-fluid">

Which results to url webs.example.com/resources/fontawesome/css/all.css and webs.example.com/images/banners/1.jpg respectively (the $banner->image holds /images/banners/1.jpg)

Which are not found for obvious reason- there is a missing part in the URL, the URL does not contain project name as it should.

So correct URL are (<project_name> is dynamic part of URL, like myProject)

webs.example.com/<project_name>/resources/fontawesome/css/all.css
webs.example.com/<project_name>/images/banners/1.jpg

And as I see it, it may be caused by bad NGINX configuration. Because when I try to hardcode web root path to its /public folder (/var/www/laravel/myProject/public), everything works fine. So I am seeking an option to achieve this by default for all projects in some folder. On Apache, this worked fine


In Apache (from which I am migrating to NGINX and in which everything works fine), there is a simple way how to solve it by adding .htaccess with simple redirecting everything on root to /public. NGINX has no such possibility.

To make it more clear, I can pass the default .htaccess content on project root folder (on the same level as public folder) that handles this to public redirection

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

    RewriteEngine On

    RewriteCond %{REQUEST_FILENAME} -d [OR]
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteRule ^ ^$1 [N]

    RewriteCond %{REQUEST_URI} (.w+$) [NC]
    RewriteRule ^(.*)$ public/$1


    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ server.php

</IfModule>

And then there is another .htaccess right in the /public folder as well

<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]

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

2

Answers


  1. So if I’m understanding correctly, the problem isn’t with your web server at all – it’s the URLs being generated by your Laravel app, which are missing the subdirectory?

    First thing you should know is that the approach you’re using to serve multiple apps is not recommended or supported by Laravel.

    Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory".

    A much better (and properly supported) solution would be serving from a subdomain, e.g. https://my-new-project.webs.example.com/. This could be easily done using the same approach you’re taking now, but using the hostname instead of the path to determine which app to serve. See this Server Fault answer for an example of parsing the hostname components.

    That said, if everything else has been figured out, you should be able to resolve the issue with generated URLs by simply fixing the APP_URL variable in .env to include the subdirectory.

    APP_URL="https://webs.example.com/my-new-project"
    

    But again, I would strongly discourage this subdirectory approach. It’s fragile and likely to be broken by changes in the Laravel codebase.

    Login or Signup to reply.
  2. Did you try to customize the ASSET_URL for every subdirectory ?
    Maybe you could set directly the env variable on top of your index.php files : $_ENV['ASSET_URL'] = 'https://webs.example.com/my-new-project';
    But the best way would be to set it dynamically based on the subdirectory name with config(['app.asset_url' => config('app.url') . $subdirectory]);

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search