Skip to content

Install Laravel on shared hosting without terminal access ​

On most shared hosting plans you cannot open a terminal or use SSH. This guide shows how to install a Laravel application using only the APNCloud Hosting Panel: File Manager, PHP Settings and phpMyAdmin. You prepare the project on your own computer, upload it, and keep Laravel's private files outside the web folder.

Before you begin ​

  • A Laravel project that already works on your computer, and its PHP version and required extensions.
  • Composer and Node.js on your own computer, if the project uses them. You run these locally, not on the server.
  • A domain already added to your hosting account, with HTTPS enabled.
  • A backup of any existing site on the domain. Installing into a folder that contains a live site can overwrite it.
  • Confirmation that your plan supports the PHP version and extensions your Laravel version needs. Ask support if you are unsure. A panel icon does not prove a feature is included.

Why the server layout matters

Laravel's .env file holds database passwords and the application key. The folders app, config, storage and vendor hold private code and logs. Only the contents of Laravel's public folder should be reachable from a browser. Method A below keeps everything else outside the web folder. Use method B only if you cannot do that.

text
(the folder that contains public_html)
├── laravel_app/        <-- the whole Laravel project, private
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── storage/
│   ├── vendor/
│   └── .env
└── public_html/        <-- web folder, only Laravel's "public" contents
    ├── .htaccess
    ├── index.php
    ├── build/
    └── storage/

The name laravel_app is only an example. The web folder is the document root shown for your domain in the panel. It may be called public_html, and it may sit inside a folder named after your domain.

Steps ​

Prepare the project on your computer ​

  1. Use the same PHP version on your computer as you will select on the server. Then install the production dependencies and build the frontend files. Do not run config:cache or route:cache here, because they store paths from your computer.

    bash
    composer install --no-dev --prefer-dist --optimize-autoloader
    npm ci
    npm run build
  2. Check that your project does not need a command the server cannot run. Anything that normally needs php artisan has to be done locally first. This includes generating an application key (php artisan key:generate --show), running migrations and seeding data.

  3. Create one ZIP file of the project. Include vendor and the built public/build folder. Leave out .env, .git, node_modules, storage/logs/* and any local cache or test data.

Create the database ​

  1. In the panel, create a MySQL or MariaDB database and a separate database user. Write down the full database name, user name and host exactly as the panel shows them. They often include a prefix. See Databases.
  2. On your computer, export your local database as an .sql file. Open phpMyAdmin from the panel and import it into the new database. This replaces php artisan migrate. If the import fails because of file size, ask support about the upload limit or split the file.

Upload the files ​

  1. Open File Manager. Go to the folder that contains public_html. Create a folder named laravel_app there, outside public_html.
  2. Upload your ZIP file into laravel_app and use Extract. Delete the ZIP file afterwards. See Files.
  3. Move the contents of laravel_app/public (not the folder itself) into public_html. This includes index.php, .htaccess and the build folder. If .htaccess is not visible, turn on the option to show hidden files.
  4. Make sure public_html contains no other copy of the project, and no .env.

Point the entry file to the private folder ​

  1. Open public_html/index.php with the File Manager editor. Every path that starts with __DIR__.'/../ must point into laravel_app. For Laravel 11 or newer, the file should look like this. Compare it with your own file, and keep any extra lines your version has.

    php
    <?php
    
    use Illuminate\Http\Request;
    
    define('LARAVEL_START', microtime(true));
    
    if (file_exists($maintenance = __DIR__.'/../laravel_app/storage/framework/maintenance.php')) {
        require $maintenance;
    }
    
    require __DIR__.'/../laravel_app/vendor/autoload.php';
    
    $app = require_once __DIR__.'/../laravel_app/bootstrap/app.php';
    $app->usePublicPath(__DIR__);
    
    $app->handleRequest(Request::capture());

    On Laravel 10 or older, change the same three paths, and add the line $app->usePublicPath(__DIR__); straight after the line that creates $app.

    The usePublicPath line tells Laravel where the web folder really is. Without it, Vite assets and public_path() look in the wrong place.

Create the environment file ​

  1. In laravel_app, create a file named .env in File Manager. Fill it with production values, not the values from your computer.

    dotenv
    APP_NAME="My App"
    APP_ENV=production
    APP_KEY=base64:paste-the-key-from-your-local-project
    APP_DEBUG=false
    APP_URL=https://example.com
    
    DB_CONNECTION=mysql
    DB_HOST=localhost
    DB_DATABASE=your_database_name
    DB_USERNAME=your_database_user
    DB_PASSWORD=your_database_password

    Use the database values you noted when you created the database. For a live site that already has users or encrypted data, use the same APP_KEY as before. A new key makes existing sessions and encrypted data unreadable. Never put .env in public_html.

Set the PHP version and permissions ​

  1. In PHP Settings, choose the PHP version your Laravel version supports, and enable the extensions it needs.
  2. Make laravel_app/storage and laravel_app/bootstrap/cache writable by the application. Normally 755 for folders and 644 for files is enough. Do not use 777. If Laravel reports that it cannot write, ask support which permission your server uses instead of opening the folders to everyone.

Make uploaded files available ​

  1. Without a terminal you cannot run php artisan storage:link. Instead, tell Laravel to save public files straight into the web folder. In laravel_app/config/filesystems.php, set the public disk like this:

    php
    'public' => [
        'driver' => 'local',
        'root' => public_path('storage'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],

    Copy your existing uploaded files from storage/app/public on your computer into public_html/storage. Files saved by the application from now on go to the same place.

Test the site ​

  1. Open your domain in the browser, then log in, submit a form, upload a file and open a page that uses CSS and JavaScript from build.
  2. Check that private files are not reachable. These addresses must not show any file content. The result should be 403, 404 or an error page:
    • https://example.com/.env
    • https://example.com/vendor/autoload.php
    • https://example.com/composer.json
    • https://example.com/storage/logs/laravel.log
    • https://example.com/laravel_app/.env

Method B: the whole project inside the web folder ​

Use this only if File Manager cannot create folders outside the web folder and support cannot change your document root to a public folder. It is less safe. Anything that the web server does not block can be downloaded, so a mistake can expose .env and your code.

  1. Upload and extract the whole project into the web folder, with Laravel's public folder inside it. Keep .env in the project root. Skip the index.php change from Point the entry file to the private folder (the default file already works), but still make the filesystems.php change from Make uploaded files available. Laravel's own public/.htaccess stays unchanged.

  2. Create the following .htaccess file in the web folder. It sends every request to the public folder and blocks the private files.

    apache
    # Do not list folder contents
    Options -Indexes
    
    <IfModule mod_rewrite.c>
        RewriteEngine On
    
        # Block hidden files and folders, but allow .well-known (needed for SSL checks)
        RewriteRule (^|/)\.(?!well-known(/|$)) - [F,L]
    
        # Block private Laravel folders
        RewriteRule ^(app|bootstrap|config|database|resources|routes|vendor|tests)(/|$) - [F,L,NC]
    
        # Block private files in the project root
        RewriteRule ^(artisan|composer\.(json|lock)|package(-lock)?\.json|phpunit\.xml|vite\.config\.[^/]+|webpack\.mix\.js|server\.php)$ - [F,L,NC]
    
        # Do not allow browsers to request /public directly
        RewriteCond %{THE_REQUEST} "\s/+public(?:[/?\s]|$)" [NC]
        RewriteRule ^public(?:/|$) - [F,L,NC]
    
        # Keep the Authorization and XSRF headers
        RewriteCond %{HTTP:Authorization} .
        RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
        RewriteCond %{HTTP:X-XSRF-TOKEN} .
        RewriteRule ^ - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-TOKEN}]
    
        # Send everything else to Laravel's public folder
        RewriteCond %{REQUEST_URI} !^/public(?:/|$) [NC]
        RewriteRule ^(.*)$ public/$1 [L]
    </IfModule>
    
    # Deny sensitive file types
    <FilesMatch "(?i)(^\.|\.env(\..*)?$|\.log$|\.sql$|\.sqlite$|\.bak$|\.backup$|\.old$|\.ini$|\.conf$|\.ya?ml$)">
        Require all denied
    </FilesMatch>
  3. Run the same checks as in Test the site. If any private address still shows content, remove the project from the web folder and use method A. Do not leave it online.

Method B depends on the web server

These rules work only where the server reads .htaccess files and allows these directives. If your site uses a web server setup that ignores .htaccess, the rules do nothing and private files may be exposed. Ask support to confirm before you rely on method B. Apache rewrite rules do not configure Nginx.

After the site is live ​

  • Scheduled tasks: Laravel's scheduler needs one cron job that runs schedule:run every minute. Create it in Cron Jobs if your plan includes it. Use the PHP path and the full path to artisan that your account shows or that support gives you. See Cron jobs.
  • Queues: shared hosting normally cannot keep a queue worker running. Use the sync queue driver for light work. For background jobs, ask whether your plan supports them, or use a VPS.
  • Updates: upload changed files through File Manager. If composer.json changed, run composer install --no-dev on your computer and upload the new vendor folder. Clear old cached views by deleting the files in storage/framework/views. Make a backup first.
  • Logs: if something fails, download laravel_app/storage/logs/laravel.log and read the last error. Never share the log publicly, because it can contain private data.

If something goes wrong ​

  • Blank page or HTTP 500: open storage/logs/laravel.log. Common causes are the wrong PHP version, a missing extension, the index.php paths from Point the entry file to the private folder, a missing vendor folder, or storage folders that are not writable. Keep APP_DEBUG=false on the live site.
  • 500 error right after adding .htaccess: some servers do not allow every directive. Remove the Options -Indexes line and try again, then ask support.
  • "No application encryption key has been specified": APP_KEY is missing or empty in .env.
  • Database error: compare the database name, user and host with the panel. Names often have a prefix. Check that the user has access to the database.
  • CSS or JavaScript is missing: check that public_html/build exists with manifest.json inside, and that usePublicPath is in index.php. Also check that APP_URL uses https://.
  • Pages other than the home page return 404: public_html/.htaccess is missing, or the server is not reading it. Ask support.
  • Uploaded images do not load: check the filesystems.php change and that the files exist in public_html/storage.
  • The page shows Index of / or a folder list: the document root or index.php is wrong. Do not leave folder listing on.

Last reviewed ​

2026-10-28