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.
How the files will be arranged (method A, recommended)
(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
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:cacheorroute:cachehere, because they store paths from your computer.bashcomposer install --no-dev --prefer-dist --optimize-autoloader npm ci npm run buildCheck that your project does not need a command the server cannot run. Anything that normally needs
php artisanhas to be done locally first. This includes generating an application key (php artisan key:generate --show), running migrations and seeding data.Create one ZIP file of the project. Include
vendorand the builtpublic/buildfolder. Leave out.env,.git,node_modules,storage/logs/*and any local cache or test data.
Create the database
- 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.
- On your computer, export your local database as an
.sqlfile. Open phpMyAdmin from the panel and import it into the new database. This replacesphp artisan migrate. If the import fails because of file size, ask support about the upload limit or split the file.
Upload the files
- Open File Manager. Go to the folder that contains
public_html. Create a folder namedlaravel_appthere, outsidepublic_html. - Upload your ZIP file into
laravel_appand use Extract. Delete the ZIP file afterwards. See Files. - Move the contents of
laravel_app/public(not the folder itself) intopublic_html. This includesindex.php,.htaccessand thebuildfolder. If.htaccessis not visible, turn on the option to show hidden files. - Make sure
public_htmlcontains no other copy of the project, and no.env.
Point the entry file to the private folder
Open
public_html/index.phpwith the File Manager editor. Every path that starts with__DIR__.'/../must point intolaravel_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
usePublicPathline tells Laravel where the web folder really is. Without it, Vite assets andpublic_path()look in the wrong place.
Create the environment file
In
laravel_app, create a file named.envin File Manager. Fill it with production values, not the values from your computer.dotenvAPP_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_passwordUse 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_KEYas before. A new key makes existing sessions and encrypted data unreadable. Never put.envinpublic_html.
Set the PHP version and permissions
- In PHP Settings, choose the PHP version your Laravel version supports, and enable the extensions it needs.
- Make
laravel_app/storageandlaravel_app/bootstrap/cachewritable by the application. Normally755for folders and644for files is enough. Do not use777. 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
Without a terminal you cannot run
php artisan storage:link. Instead, tell Laravel to save public files straight into the web folder. Inlaravel_app/config/filesystems.php, set thepublicdisk 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/publicon your computer intopublic_html/storage. Files saved by the application from now on go to the same place.
Test the site
- 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. - 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/.envhttps://example.com/vendor/autoload.phphttps://example.com/composer.jsonhttps://example.com/storage/logs/laravel.loghttps://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.
Upload and extract the whole project into the web folder, with Laravel's
publicfolder inside it. Keep.envin the project root. Skip theindex.phpchange from Point the entry file to the private folder (the default file already works), but still make thefilesystems.phpchange from Make uploaded files available. Laravel's ownpublic/.htaccessstays unchanged.Create the following
.htaccessfile in the web folder. It sends every request to thepublicfolder 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>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:runevery minute. Create it in Cron Jobs if your plan includes it. Use the PHP path and the full path toartisanthat your account shows or that support gives you. See Cron jobs. - Queues: shared hosting normally cannot keep a queue worker running. Use the
syncqueue 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.jsonchanged, runcomposer install --no-devon your computer and upload the newvendorfolder. Clear old cached views by deleting the files instorage/framework/views. Make a backup first. - Logs: if something fails, download
laravel_app/storage/logs/laravel.logand 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, theindex.phppaths from Point the entry file to the private folder, a missingvendorfolder, or storage folders that are not writable. KeepAPP_DEBUG=falseon the live site. - 500 error right after adding
.htaccess: some servers do not allow every directive. Remove theOptions -Indexesline and try again, then ask support. - "No application encryption key has been specified":
APP_KEYis 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/buildexists withmanifest.jsoninside, and thatusePublicPathis inindex.php. Also check thatAPP_URLuseshttps://. - Pages other than the home page return 404:
public_html/.htaccessis missing, or the server is not reading it. Ask support. - Uploaded images do not load: check the
filesystems.phpchange and that the files exist inpublic_html/storage. - The page shows
Index of /or a folder list: the document root orindex.phpis wrong. Do not leave folder listing on.
Related articles
Last reviewed
2026-10-28
