Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bacordioroger's avatar

403 Forbidden Error Symlink

Hi, I've researched many tutorials how to deploy a laravel project. I'm using bitbucket, thus I want my laravel project to be retained so I can push easily. What I did was create a folder named projects, then another folder inside it named hrmis, then cloned my laravel project there from github/bitbucket. After that, I delete the public_html folder from root directory of the website, then symlinked my --public-- folder to public_html using the code:

ln -s ~projects/hrmis/public ~/public_html

But I'm getting 403 forbidden error. I tried setting all my files' permission temporarily to 777 but got no luck. Do you have any suggestions?

0 likes
2 replies
aurawindsurfing's avatar

Hi,

I know it is quite boring but really really really try https://forge.laravel.com/ I love it and I do not want to get back to install it yourself times.

Back to your question however. For me it looks like the simplest way to solve your problem is not to edit the project but to edit ngnix config (sites-available) and point it to the correct laravel directory. It should look something like this:

server {

    listen 80;
    listen [::]:80;

    server_name hrmis.test;
    root /projects/hrmis/public;    
    index index.php index.html index.htm;

    location / {
         try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_pass php-upstream;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

Hope it helps!

1 like
Snapey's avatar

This is my build shell script for one of my projects. you may be able to take something from this;

#!/bin/sh

UNIX_TIME=$(date +%s)

DEPLOYMENT_DIRECTORY=$UNIX_TIME

mkdir -p $DEPLOYMENT_DIRECTORY
git clone --depth 1 [email protected]:novate/speakernetv6.git $DEPLOYMENT_DIRECTORY

cd $DEPLOYMENT_DIRECTORY

cp ../master/.env .

composer install --no-dev

cd ..
echo emptying storage
rm -R $DEPLOYMENT_DIRECTORY/storage/app
#rm -R $DEPLOYMENT_DIRECTORY/storage/framework
rm -R $DEPLOYMENT_DIRECTORY/storage/logs
rm -R $DEPLOYMENT_DIRECTORY/storage/medialibrary
rm -R $DEPLOYMENT_DIRECTORY/storage/debugbar

chgrp -R www-data $DEPLOYMENT_DIRECTORY/storage $DEPLOYMENT_DIRECTORY/bootstrap/cache
chmod -R ug+rwx $DEPLOYMENT_DIRECTORY/storage $DEPLOYMENT_DIRECTORY/bootstrap/cache

#link to the latest deployment from Live
ln -s -n -f -T $DEPLOYMENT_DIRECTORY Live

# create a link from storage/app to public
ln -s -n -f -T /var/www/html/speakernet.co.uk/master/storage/app $DEPLOYMENT_DIRECTORY/storage/app

cd Live
echo link public to storage
php artisan storage:link

echo route:cache command
php artisan route:cache

echo config:cache command
php artisan config:cache

cd ..
echo Done!
echo Migrations required?
echo

exit

  1. The script uses the unix time as the name for a deployment folder.
  2. It clones the repository into the deployment folder
  3. It runs composer to install all dependencies
  4. copies the .env file from a master folder
  5. creates a symlink from deployment into the master folder for storage
  6. sets the permissions of everything
  7. changes into the Live folder and clears laravel caches

My Apache virtualHost points to the Live folder for its directory

Deployment (running ./build.sh from the project folder) takes about 5 seconds and the changeover is immediate. Any logged in users will be logged out because their sessions will be lost.

I can find a quiet time to switch and use Google Analytics real-time view to see if I will upset anyone.

The script has been robust but of course needs to be adapted to your own circumstances.

1 like

Please or to participate in this conversation.