{"id":446,"date":"2025-08-27T22:05:34","date_gmt":"2025-08-27T22:05:34","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=446"},"modified":"2025-08-27T22:05:36","modified_gmt":"2025-08-27T22:05:36","slug":"how-to-deploy-laravel-to-shared-hosting-step-by-step","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-deploy-laravel-to-shared-hosting-step-by-step\/","title":{"rendered":"How to Deploy Laravel to Shared Hosting (Step by Step)"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>How to Deploy Laravel to Shared Hosting (Step by Step)<\/strong><\/h2>\n\n\n\n<p>Not every project requires cloud infrastructure like AWS or DigitalOcean. Sometimes, you just need to put a Laravel 12 app on traditional <strong>shared hosting<\/strong>. While shared hosting environments are limited (no root access, limited SSH, restricted PHP extensions), you can still successfully deploy Laravel apps with a few adjustments. In this guide, we\u2019ll cover a step-by-step process to deploy Laravel on shared hosting the right way.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 \u2014 Prepare Your Laravel App Locally<\/strong><\/h2>\n\n\n\n<p>Before uploading to your hosting server, you should optimize and clean your Laravel project.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># On your local machine<\/span>\ncomposer install --optimize-autoloader --no-dev\nnpm run build\n\nphp artisan config:cache\nphp artisan route:cache\nphp artisan view:cache<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This installs only production dependencies, compiles frontend assets (via Vite), and caches configuration, routes, and views for speed. By doing this locally, you avoid heavy Composer or NPM installs on your shared host (where resources are often limited).<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 \u2014 Adjust the Public Directory<\/strong><\/h2>\n\n\n\n<p>Shared hosting usually points the web root to <code>public_html\/<\/code>. Laravel, however, expects <code>public\/<\/code> as its entry point. To fix this, move your project and update paths.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># Folder structure after moving<\/span>\n\/my-app\n  \/app\n  \/bootstrap\n  \/config\n  \/database\n  \/public (renamed or moved to \/public_html)\n  \/resources\n  \/routes\n  \/vendor\n  .env\n  artisan<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Move everything <em>except<\/em> the <code>public\/<\/code> folder outside of <code>public_html\/<\/code>. Then, copy the contents of <code>public\/<\/code> into <code>public_html\/<\/code>. This keeps your sensitive files out of the web root while still serving assets and <code>index.php<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 \u2014 Update index.php Paths<\/strong><\/h2>\n\n\n\n<p>Because <code>public\/index.php<\/code> is now inside <code>public_html\/<\/code>, you need to update the paths to point to your Laravel root.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ public_html\/index.php<\/span>\n\n<span class=\"hljs-keyword\">require<\/span> <span class=\"hljs-keyword\">__DIR__<\/span>.<span class=\"hljs-string\">'\/..\/vendor\/autoload.php'<\/span>;\n\n$app = <span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span>.<span class=\"hljs-string\">'\/..\/bootstrap\/app.php'<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>..\/<\/code> ensures Laravel loads from the parent directory (where <code>vendor\/<\/code> and <code>bootstrap\/<\/code> are stored). Without this, your app will break on shared hosting.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 \u2014 Configure .env for Shared Hosting<\/strong><\/h2>\n\n\n\n<p>Edit your <code>.env<\/code> file to match the hosting provider\u2019s database credentials and environment settings.<\/p>\n\n\n<!-- DomainException(0): Unknown language: \"dotenv\" -->APP_ENV=production\nAPP_KEY=base64:xxxxxx\nAPP_DEBUG=false\nAPP_URL=https:\/\/yourdomain.com\n\nDB_CONNECTION=mysql\nDB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=your_db\nDB_USERNAME=your_user\nDB_PASSWORD=your_pass\n\n\n<p>Most shared hosts provide database info in cPanel (MySQL Databases section). Always set <code>APP_DEBUG=false<\/code> in production to avoid leaking sensitive details.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 \u2014 Upload Your Project<\/strong><\/h2>\n\n\n\n<p>Zip your project locally (excluding <code>node_modules<\/code>) and upload via FTP or cPanel\u2019s File Manager. Then extract the zip on the server.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># Example of excluding node_modules<\/span>\nzip -r app.zip . -x <span class=\"hljs-string\">\"node_modules\/*\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Extract the archive so the Laravel core files are one level above <code>public_html<\/code>. Ensure <code>storage<\/code> and <code>bootstrap\/cache<\/code> directories are writable (set permissions to 775).<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 \u2014 Configure .htaccess<\/strong><\/h2>\n\n\n\n<p>Most shared hosting environments run Apache. Add or edit <code>public_html\/.htaccess<\/code> for Laravel\u2019s routing.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Apache\" data-shcb-language-slug=\"apache\"><span><code class=\"hljs language-apache\"><span class=\"hljs-section\">&lt;IfModule mod_rewrite.c&gt;<\/span>\n    <span class=\"hljs-section\">&lt;IfModule mod_negotiation.c&gt;<\/span>\n        <span class=\"hljs-attribute\"><span class=\"hljs-nomarkup\">Options<\/span><\/span> -MultiViews -Indexes\n    <span class=\"hljs-section\">&lt;\/IfModule&gt;<\/span>\n\n    <span class=\"hljs-attribute\"><span class=\"hljs-nomarkup\">RewriteEngine<\/span><\/span> <span class=\"hljs-literal\">On<\/span>\n    <span class=\"hljs-attribute\"><span class=\"hljs-nomarkup\">RewriteCond<\/span><\/span> <span class=\"hljs-variable\">%{REQUEST_FILENAME}<\/span> !-d\n    <span class=\"hljs-attribute\"><span class=\"hljs-nomarkup\">RewriteCond<\/span><\/span> <span class=\"hljs-variable\">%{REQUEST_FILENAME}<\/span> !-f\n    <span class=\"hljs-attribute\"><span class=\"hljs-nomarkup\">RewriteRule<\/span><\/span> ^ index.php<span class=\"hljs-meta\"> &#91;L]<\/span>\n<span class=\"hljs-section\">&lt;\/IfModule&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Apache<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">apache<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This routes all non-file requests through <code>index.php<\/code>, which boots Laravel. Without it, URLs like <code>\/dashboard<\/code> will 404.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7 \u2014 Run Migrations and Storage Link<\/strong><\/h2>\n\n\n\n<p>If you have SSH access, run migrations and link the storage folder. If not, you may need to use a route-based trick for one-time setup.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># If SSH available<\/span>\nphp artisan migrate --force\nphp artisan storage:link<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>--force<\/code> ensures migrations run in production. <code>storage:link<\/code> makes uploaded files accessible from <code>\/storage<\/code> in the browser. On hosts without SSH, you can temporarily create a route\/controller to call <code>Artisan::call('migrate')<\/code> and then remove it for security.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>8 \u2014 Deployment Check UI<\/strong><\/h2>\n\n\n\n<p>Since shared hosting often limits server monitoring, you can build a minimal admin-only status page to confirm database and storage health.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\nRoute::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>, <span class=\"hljs-string\">'can:viewAdmin'<\/span>])-&gt;get(<span class=\"hljs-string\">'\/status'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'admin.shared-status'<\/span>, &#91;\n        <span class=\"hljs-string\">'db'<\/span> =&gt; DB::connection()-&gt;getDatabaseName(),\n        <span class=\"hljs-string\">'storageWritable'<\/span> =&gt; is_writable(storage_path(<span class=\"hljs-string\">'app'<\/span>)),\n    ]);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This route checks whether the database connection works and if the storage directory is writable.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-comment\">&lt;!-- resources\/views\/admin\/shared-status.blade.php --&gt;<\/span>\n@extends('layouts.app')\n@section('content')\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mb-4\"<\/span>&gt;<\/span>Shared Hosting Status<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"list-group\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"list-group-item\"<\/span>&gt;<\/span>Database: {{ $db ? $db : 'Not connected' }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"list-group-item\"<\/span>&gt;<\/span>Storage Writable: {{ $storageWritable ? 'Yes' : 'No' }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n@endsection<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This simple page reassures you that your Laravel app is properly running on shared hosting, without needing advanced server access.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Deploying a Laravel 12 app on shared hosting requires some manual adjustments: moving the <code>public<\/code> folder into <code>public_html<\/code>, fixing <code>index.php<\/code> paths, setting the correct <code>.env<\/code>, and configuring <code>.htaccess<\/code>. Once migrations and storage links are in place, your project will run smoothly\u2014even in limited environments. While shared hosting isn\u2019t ideal for large-scale apps, it\u2019s a cost-effective solution for small projects and MVPs.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/laravel-nginx-best-practices-for-production\">Laravel &amp; Nginx: Best Practices for Production<\/a> (#56) \u2014 applies if your shared host uses Nginx instead of Apache.<\/li>\n<li><a href=\"\/blog\/laravel-deployment-checklist-for-2025\">Laravel Deployment Checklist for 2025<\/a> (#58) \u2014 a final checklist before going live.<\/li>\n<li><a href=\"\/blog\/automating-laravel-deployments-with-deployer\">Automating Laravel Deployments with Deployer<\/a> (#59) \u2014 for when you move beyond shared hosting.<\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>How to Deploy Laravel to Shared Hosting (Step by Step) Not every project requires cloud infrastructure like AWS or DigitalOcean. Sometimes, you just need to put a Laravel 12 app on traditional shared hosting. While shared hosting environments are limited (no root access, limited SSH, restricted PHP extensions), you can still successfully deploy Laravel apps [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[74,71,81],"class_list":["post-446","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-deployment","tag-devops","tag-shared-hosting"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/446","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=446"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/446\/revisions"}],"predecessor-version":[{"id":449,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/446\/revisions\/449"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/450"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=446"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=446"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=446"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}