Ravindra BagaleCourses & study guides

16. Live Project: Building a Reels App with EC2, S3 and RDS

16.10 Deployment: Nginx, PHP Settings and Going Live

Code tayar aahe – aata Nginx aani PHP-FPM la sangu. Upload size teen thikani jultat, lakshat theva.

deploy/reels.conf

# /etc/nginx/conf.d/reels.conf  (Ubuntu: /etc/nginx/sites-available/reels + symlink)
server {
    listen 80;
    server_name reels.yourdomain.com;
    root  /var/www/reels/public;          # ONLY public/ is reachable from the web
    index index.php;

    client_max_body_size 60M;              # must be >= PHP post_max_size
    server_tokens off;

    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; media-src 'self' https://ravindra-reels-media-pune.s3.ap-south-1.amazonaws.com; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/run/php-fpm/www.sock;      # Ubuntu: unix:/run/php/php-fpm.sock
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_read_timeout 120s;
    }

    location ~ /\. { deny all; }           # .git, .env and other hidden files
}

deploy/99-reels.ini

; /etc/php.d/99-reels.ini   (Ubuntu: /etc/php/8.3/fpm/conf.d/99-reels.ini)
upload_max_filesize = 55M
post_max_size       = 58M
max_execution_time  = 120
memory_limit        = 256M
expose_php          = Off
display_errors      = Off
log_errors          = On
session.use_strict_mode = 1
cd /var/www/reels
# 1. syntax-check EVERY PHP file before going live
find . -path ./vendor -prune -o -name '*.php' -print -exec php -l {} \;
# 2. PHP settings
sudo cp deploy/99-reels.ini /etc/php.d/99-reels.ini          # Ubuntu: /etc/php/8.3/fpm/conf.d/
sudo service php-fpm restart                                 # Ubuntu: php8.3-fpm
# 3. Nginx site
sudo cp deploy/reels.conf /etc/nginx/conf.d/reels.conf       # Ubuntu: sites-available + ln -s
sudo nginx -t && sudo service nginx reload
# 4. permissions: code read-only for the web server
sudo chown -R ec2-user:ec2-user /var/www/reels
sudo chown root:apache /var/www/reels/config.php && sudo chmod 640 /var/www/reels/config.php
# 5. smoke test
curl -I http://localhost/login.php -H "Host: reels.yourdomain.com"      # expect 200
curl -s http://localhost/config.php -H "Host: reels.yourdomain.com" -o /dev/null -w "%{http_code}\n"  # expect 404

Expected php -l output:

./src/bootstrap.php
No syntax errors detected in ./src/bootstrap.php
./public/upload.php
No syntax errors detected in ./public/upload.php
./public/feed.php
No syntax errors detected in ./public/feed.php
...

Why this matters for security

Pointing root at public/ (not the project folder) is what keeps config.php, vendor/ and schema.sql out of reach. server_tokens off and expose_php = Off hide version numbers that scanners use to pick exploits, and location ~ /\. blocks .git and .env – a very common finding in real penetration tests.

Ravindra Bagale's Tip

Students write /var/www/reels as the Nginx root, not /var/www/reels/public – and then https://site/config.php... Nginx runs the PHP, so the password isn't shown, but schema.sql and composer.json can be downloaded. After deploying, always do the curl test from step 5 – it must return 404.

Lab

Deploy the app, run the php -l loop until every file reports no syntax errors, and complete the smoke tests. Then register, post a video and see it in the feed using http://<Elastic-IP> with cookie_secure temporarily false.