Foundation — Servers & Web Servers
Understand how the internet works, master Linux basics, configure Apache2 and Nginx for any web project.
Section 1 — Introduction to Servers
1.1 What Is a Server?
Think of a server as a powerful computer that runs 24/7, waiting for people to connect and ask for things — web pages, images, database data, etc.
Real-world analogy: A restaurant kitchen. The client is the customer. The server is the kitchen. When the customer (browser) orders food (a web page), the kitchen (server) prepares it and sends it to the table (browser).
| Your Laptop | A Server |
|---|---|
| Turned off at night | Runs 24 hours a day, 7 days a week |
| Used by 1 person | Used by hundreds to millions simultaneously |
| Has a screen & keyboard | Usually no screen — managed remotely via SSH |
| Consumer hardware | High-performance hardware (more RAM, CPU, RAID) |
| Runs Windows or macOS | Usually runs Linux |
| Located at your desk | Located in a Data Center |
Linux Distributions
90%+ of all web servers run Linux. Linux comes in different "flavors" called distributions (distros):
| Distribution | Used For |
|---|---|
| Ubuntu | Web servers, development (most popular for beginners) |
| Debian | Stable production servers |
| CentOS / AlmaLinux | Enterprise environments |
| Alpine | Docker containers (very minimal) |
We primarily use Ubuntu 20.04 LTS or Ubuntu 22.04 LTS on all production and staging servers.
1.2 VPS & Cloud Hosting
What Is a VPS?
VPS = Virtual Private Server. A VPS is a virtual machine hosted in a data center. A physical server is divided into multiple virtual servers using virtualization software (KVM, VMware).
Physical Server (64GB RAM, 32 CPU cores)
├── VPS 1 (4GB RAM, 2 CPU) → Your client's live server
├── VPS 2 (8GB RAM, 4 CPU) → Another client's server
├── VPS 3 (2GB RAM, 1 CPU) → Staging server
└── VPS 4 (16GB RAM, 8CPU) → Large e-commerce server
| Feature | Shared Hosting | VPS |
|---|---|---|
| Control | Very Limited | Full Root Access |
| Performance | Affected by other users | Dedicated resources |
| Price | $3–$10/month | $5–$80/month |
| Customization | Almost none | Fully customizable |
| Use case | Simple WordPress sites | Business apps, custom setups |
Local Machine vs Production Server
Your Local Machine Production Server
───────────────── ──────────────────
Windows / macOS / Ubuntu Ubuntu Linux (headless)
XAMPP / Laragon / WAMP Apache2 / Nginx + PHP-FPM
MySQL (local) MySQL (production, secured)
localhost:8000 yourdomain.com
Only you access it Thousands of users access it
.env has debug=true .env has debug=false (NEVER expose)
Loose file permissions (777 OK) Strict permissions (644, 755)
No SSL needed SSL mandatory
What works on your local machine may NOT work on the server immediately. Always check permissions, paths, environment variables, and PHP versions.
1.3 Client–Server Architecture
Every website interaction follows this pattern:
Browser (Client)
│
│ HTTP Request: GET /index.php HTTP/1.1
▼
Web Server (Apache2 / Nginx) ← Port 80 / 443
│
│ Dynamic request
▼
Application Layer (PHP / Node.js / Python)
│
│ Database query
▼
Database (MySQL / MongoDB / Redis)
│
│ Response data
▼
Application → Web Server → Browser
└── Browser renders the HTML, CSS, JS
1.4 What Happens When You Type a Domain?
This is one of the most common interview questions. Know this process deeply.
User types: https://www.example.com
│
▼
1. Browser Cache Check
│
▼
2. OS hosts file check (/etc/hosts on Linux)
│
▼
3. DNS Resolver (ISP or Google 8.8.8.8)
│
▼
4. Root DNS → TLD Server (.com) → Authoritative DNS
└── Returns IP: 203.0.113.42
│
▼
5. Browser connects to 203.0.113.42 on Port 443
│
▼
6. TCP Handshake (SYN → SYN-ACK → ACK)
│
▼
7. SSL/TLS Handshake (certificate verification)
│
▼
8. HTTP Request sent:
GET / HTTP/1.1
Host: www.example.com
│
▼
9. Web Server matches Virtual Host / Server Block
│
▼
10. Application processes, queries DB
│
▼
11. HTTP 200 OK response + HTML sent back
│
▼
12. Browser renders the page ✓
1.5 Ports, Protocols & DNS Records
| Port | Service |
|---|---|
22 | SSH (remote access) |
80 | HTTP (web) |
443 | HTTPS (secure web) |
3306 | MySQL / MariaDB |
27017 | MongoDB |
6379 | Redis |
3000 | Node.js apps (common default) |
9000 | PHP-FPM |
25 / 587 | SMTP (email) |
| DNS Record | Purpose | Example |
|---|---|---|
| A Record | Maps domain to IPv4 | example.com → 203.0.113.42 |
| AAAA Record | Maps domain to IPv6 | example.com → 2001:db8::1 |
| CNAME Record | Alias to another domain | www → example.com |
| MX Record | Mail server | mail.example.com |
| TXT Record | Verification, SPF, DKIM | Various |
| NS Record | Nameservers for domain | ns1.cloudflare.com |
Section 2 — Linux Fundamentals
2.1 The Linux Directory Structure
Unlike Windows (C:\, D:\), Linux has ONE root directory: /. Everything branches from it.
/ ← Root (top of everything)
├── etc/ ← All system configuration files
│ ├── apache2/ ← Apache config
│ ├── nginx/ ← Nginx config
│ ├── mysql/ ← MySQL config
│ ├── ssh/sshd_config ← SSH config
│ └── crontab ← System cron jobs
├── home/ ← Home folders for regular users
│ └── ubuntu/ ← SSH user's home
├── tmp/ ← Temporary files (cleared on reboot)
├── var/
│ ├── log/ ← ALL system logs
│ │ ├── apache2/ ← Apache access + error logs
│ │ ├── nginx/ ← Nginx logs
│ │ └── syslog ← General system log
│ └── www/ ← Web files root
│ └── html/ ← Default web root
└── opt/ ← Manually installed software (Node.js via nvm)
/etc — all config files | /var/www — your website files | /var/log — all log files for debugging
2.2 Connecting via SSH
# Connect using password
ssh ubuntu@203.0.113.42
# Connect using SSH key (recommended and more secure)
ssh -i /path/to/private-key.pem ubuntu@203.0.113.42
# Connect on a different port
ssh -p 2222 ubuntu@203.0.113.42
2.3 Navigation & File Viewing
# Where am I right now?
pwd
# List files
ls -la # Long format + hidden files
ls -lh # Human-readable file sizes
ls -lt # Sort by time (newest first)
# Change directory
cd /var/www/html # Go to absolute path
cd .. # Go up one level
cd ~ # Go to home folder
cd - # Go to previous directory
# View file contents
cat /etc/nginx/nginx.conf
less /var/log/nginx/error.log # Page by page (q to quit)
tail -f /var/log/nginx/error.log # Follow live (Ctrl+C to stop)
tail -n 50 -f /var/log/apache2/error.log
2.4 File & Directory Operations
# Create directories
mkdir -p /var/www/myproject/public # -p creates parent dirs
# Create/edit files
touch index.php
nano .env
echo "APP_ENV=production" >> .env # >> appends, > overwrites
# Copy
cp -r /var/www/oldsite /var/www/newsite
# Move / rename
mv oldname.php newname.php
# Delete — VERY CAREFUL
rm filename.php
rm -rf /var/www/mysite # Deletes everything inside — no undo!
# Search files
find /var/www -name "wp-config.php"
grep -r "database_error" /var/www/mysite/
grep -n "APP_DEBUG" .env # -n shows line numbers
rm -rf does NOT have an undo. There is no Recycle Bin. Always run ls on the path first to confirm what you're deleting. NEVER run rm -rf /.
2.5 File Permissions & Ownership
Linux permissions control who can read, write, or execute a file. Every file has three sets of permissions: owner, group, others.
| Permission | Symbol | Number |
|---|---|---|
| Read | r | 4 |
| Write | w | 2 |
| Execute | x | 1 |
| No permission | - | 0 |
| Value | Meaning | Use Case |
|---|---|---|
644 | rw-r--r-- | Standard PHP/HTML files |
755 | rwxr-xr-x | Directories, scripts |
600 | rw------- | .env file (owner only) |
775 | rwxrwxr-x | Laravel storage/, bootstrap/cache/ |
777 | rwxrwxrwx | ⚠️ NEVER use on production |
# Change permissions
chmod 644 index.php
chmod -R 755 /var/www/mysite
chmod 600 .env
# Change ownership (Apache/Nginx run as www-data)
chown www-data:www-data /var/www/mysite
chown -R www-data:www-data /var/www/mysite
# Typical Laravel permissions
sudo chown -R www-data:www-data /var/www/laravel-app
sudo chmod -R 755 /var/www/laravel-app
sudo chmod -R 775 /var/www/laravel-app/storage
sudo chmod -R 775 /var/www/laravel-app/bootstrap/cache
Apache2 and Nginx run as www-data. If your files are owned by root and www-data can't read them, you get 403 Forbidden. Understanding ownership is essential.
2.6 Package Management (APT) & Service Control
# APT — Advanced Package Tool
sudo apt update # Update package list (always do this first)
sudo apt upgrade # Upgrade all packages
sudo apt install nginx php8.2 git # Install packages
sudo apt remove nginx # Remove package
sudo apt purge nginx # Remove + delete config files
sudo apt autoremove # Remove unused dependencies
# systemctl — Service Management
sudo systemctl status nginx # Check status
sudo systemctl start nginx # Start service
sudo systemctl stop nginx # Stop service
sudo systemctl restart nginx # Full restart
sudo systemctl reload nginx # Graceful reload (preferred for web servers)
sudo systemctl enable nginx # Auto-start on boot
sudo systemctl disable nginx # Disable auto-start
# View service logs
sudo journalctl -u nginx -f # Follow logs in real-time
sudo journalctl -u nginx -n 50 # Last 50 lines
Disk & Memory Usage
df -h # Disk usage (human readable)
du -sh /var/www/* # Size of each website directory
free -h # Memory usage
top # Real-time process monitor
htop # Better visual version (install with apt)
ps aux | grep nginx # Find running process
2.7 Cron Jobs — Scheduled Tasks
Cron jobs are automated tasks that run on a schedule.
# Edit cron jobs for current user
crontab -e
# Cron syntax:
# ┌─── minute (0-59)
# │ ┌─── hour (0-23)
# │ │ ┌─── day of month (1-31)
# │ │ │ ┌─── month (1-12)
# │ │ │ │ ┌─── day of week (0-7, Sun=0)
# * * * * * command
# Examples:
* * * * * php /var/www/myapp/artisan schedule:run # Every minute (Laravel)
0 2 * * * mysqldump mydb > /backup/mydb.sql # Daily at 2 AM
0 0 * * 0 certbot renew # Weekly SSL renewal
0 * * * * php /var/www/mysite/cron.php # Every hour
Section 3 — Apache2 Complete Guide
Apache HTTP Server (Apache2) is the world's most popular web server software, running since 1995. Its job: listen for HTTP/HTTPS requests, find the right files or pass the request to PHP, and send the response back.
3.1 Installation & Directory Structure
sudo apt update
sudo apt install apache2
apache2 -v # Check version
sudo systemctl status apache2
sudo apache2ctl configtest # Test config (should output: Syntax OK)
/etc/apache2/
├── apache2.conf ← Main configuration file
├── ports.conf ← Which ports Apache listens on
├── sites-available/ ← All virtual host config files
│ ├── 000-default.conf ← Default site
│ └── mysite.conf ← Your site config
├── sites-enabled/ ← SYMLINKS to active sites
├── mods-available/ ← All available modules
└── mods-enabled/ ← Enabled modules (symlinks)
/var/log/apache2/
├── access.log ← Every request (IP, URL, status code)
└── error.log ← All errors ← CHECK THIS FIRST when debugging
Essential Modules
sudo a2enmod rewrite # URL rewriting (required for Laravel/WordPress)
sudo a2enmod ssl # HTTPS support
sudo a2enmod headers # HTTP security headers
sudo a2enmod expires # Browser caching
sudo a2enmod proxy # Reverse proxy
sudo a2enmod proxy_http # HTTP reverse proxy
sudo a2dismod autoindex # Disable directory listing (security)
sudo systemctl reload apache2 # Always reload after enabling modules
3.2 Virtual Hosts — Host Multiple Sites
A Virtual Host lets one Apache server host multiple websites on the same IP. Apache decides which site to show based on the domain in the request.
# 1. Create directory
sudo mkdir -p /var/www/mysite/public
sudo chown -R www-data:www-data /var/www/mysite
# 2. Create virtual host config
sudo nano /etc/apache2/sites-available/mysite.conf
<VirtualHost *:80>
ServerName mysite.com
ServerAlias www.mysite.com
DocumentRoot /var/www/mysite/public
ErrorLog ${APACHE_LOG_DIR}/mysite-error.log
CustomLog ${APACHE_LOG_DIR}/mysite-access.log combined
<Directory /var/www/mysite/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# 3. Enable the site
sudo a2ensite mysite.conf
# 4. Test config
sudo apache2ctl configtest # Should say: Syntax OK
# 5. Reload Apache
sudo systemctl reload apache2
3.3 Apache VirtualHost for Laravel
Laravel's DocumentRoot must point to the public/ subdirectory, NOT the project root. This prevents exposing .env, source code, and other sensitive files.
<VirtualHost *:80>
ServerName mylaravelapp.com
ServerAlias www.mylaravelapp.com
DocumentRoot /var/www/laravel-app/public
<Directory /var/www/laravel-app/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/laravel-app-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-app-access.log combined
</VirtualHost>
# Laravel permissions
sudo chown -R www-data:www-data /var/www/laravel-app
sudo chmod -R 755 /var/www/laravel-app
sudo chmod -R 775 /var/www/laravel-app/storage
sudo chmod -R 775 /var/www/laravel-app/bootstrap/cache
3.4 Apache VirtualHost for WordPress
<VirtualHost *:80>
ServerName mywordpresssite.com
ServerAlias www.mywordpresssite.com
DocumentRoot /var/www/mywordpresssite
<Directory /var/www/mywordpresssite>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/wordpress-error.log
CustomLog ${APACHE_LOG_DIR}/wordpress-access.log combined
</VirtualHost>
# Enable mod_rewrite for WordPress pretty permalinks
sudo a2enmod rewrite
sudo systemctl reload apache2
3.5 .htaccess Reference
The .htaccess file is a per-directory config that overrides the main Apache config. Requires AllowOverride All in the VirtualHost.
.htaccess for WordPress
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
.htaccess for Laravel
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
.htaccess for Security
# Deny access to .env files (CRITICAL)
<Files ".env">
Order allow,deny
Deny from all
</Files>
# Disable directory listing
Options -Indexes
# Security headers
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
</IfModule>
# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
3.6 Apache with SSL (Let's Encrypt)
# Install Certbot
sudo apt install certbot python3-certbot-apache
# Get SSL certificate (auto-configures Apache)
sudo certbot --apache -d mysite.com -d www.mysite.com
# Test auto-renewal
sudo certbot renew --dry-run
3.7 Apache Troubleshooting Quick Reference
| Error | Common Causes | Fix |
|---|---|---|
| 403 Forbidden | Wrong ownership, permissions too strict, AllowOverride not set | chown -R www-data:www-data /var/www/mysite & set AllowOverride All |
| 500 Internal Server Error | PHP syntax error, .htaccess error, missing extension | Check tail -f /var/log/apache2/error.log for the exact error |
| 404 on pretty URLs | mod_rewrite not enabled, AllowOverride None | a2enmod rewrite then set AllowOverride All |
| Wrong site shows up | VirtualHost conflict, default site catching requests | Run apache2ctl -S to see VirtualHost routing |
Section 4 — Nginx Complete Guide
Nginx (pronounced "Engine-X") is a high-performance web server that also acts as a reverse proxy, load balancer, and HTTP cache. Created to handle massive concurrent connections efficiently.
| Feature | Apache2 | Nginx |
|---|---|---|
| Architecture | Process/Thread-based | Event-driven (async) |
| Concurrency | Struggles at thousands | Handles 10,000+ connections easily |
| .htaccess support | ✅ Yes | ❌ No — config only in server blocks |
| PHP support | Native mod_php | Requires PHP-FPM (FastCGI) |
| Static files | Good | Excellent |
| Memory usage | Higher | Lower |
| Best for | PHP apps, WordPress | High traffic, Node.js, reverse proxy, Next.js |
4.1 Installation & Directory Structure
sudo apt update
sudo apt install nginx
nginx -v # Check version
sudo systemctl status nginx
sudo nginx -t # Test configuration syntax
sudo systemctl enable nginx
sudo systemctl start nginx
/etc/nginx/
├── nginx.conf ← Main configuration file
├── sites-available/ ← All server block configs
│ ├── default ← Default server block
│ └── mysite.conf ← Your site config
├── sites-enabled/ ← Symlinks to active configs
├── conf.d/ ← Additional config snippets
└── snippets/
└── fastcgi-php.conf ← PHP-FPM settings
/var/log/nginx/
├── access.log
└── error.log
4.2 Nginx Server Blocks (Virtual Hosts)
In Nginx, virtual hosts are called server blocks.
server {
listen 80;
listen [::]:80;
server_name mysite.com www.mysite.com;
root /var/www/mysite/public;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
# PHP processing
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
# Deny sensitive files
location ~ /\.env { deny all; }
location ~ /\.ht { deny all; }
access_log /var/log/nginx/mysite-access.log;
error_log /var/log/nginx/mysite-error.log;
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/
sudo nginx -t # Test
sudo systemctl reload nginx
4.3 Nginx Server Block for Laravel
server {
listen 80;
server_name laravelapp.com www.laravelapp.com;
root /var/www/laravel-app/public;
index index.php;
# Laravel URL rewriting (replaces .htaccess)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny all dotfiles (security)
location ~ /\.(?!well-known).* {
deny all;
}
error_log /var/log/nginx/laravel-error.log;
access_log /var/log/nginx/laravel-access.log;
}
4.4 Nginx Reverse Proxy for Node.js / Express
The most common use of Nginx in modern development: act as a gateway to a Node.js application.
Internet → Port 80/443 → Nginx (reverse proxy) → Port 3000 → Node.js App
server {
listen 80;
server_name nodeapp.com www.nodeapp.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
access_log /var/log/nginx/nodeapp-access.log;
error_log /var/log/nginx/nodeapp-error.log;
}
4.5 Nginx Reverse Proxy for Next.js
server {
listen 80;
server_name mynextapp.com www.mynextapp.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Serve Next.js static assets directly (performance)
location /_next/static/ {
alias /var/www/mynextapp/.next/static/;
expires 365d;
add_header Cache-Control "public, immutable";
}
}
4.6 Nginx for React App (Static Build)
server {
listen 80;
server_name myreactapp.com www.myreactapp.com;
root /var/www/myreactapp/build;
index index.html;
# Critical for React Router — prevents 404 on refresh
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets for 1 year
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Without try_files $uri $uri/ /index.html, refreshing a page like /about returns 404 because Nginx looks for a file named about on disk. This directive tells Nginx to fall back to index.html for all routes.
4.7 Nginx with SSL (Let's Encrypt)
# Install Certbot for Nginx
sudo apt install certbot python3-certbot-nginx
# Get and auto-configure SSL
sudo certbot --nginx -d mysite.com -d www.mysite.com
# Test auto-renewal
sudo certbot renew --dry-run
# Manual SSL configuration
server {
listen 80;
server_name mysite.com www.mysite.com;
return 301 https://$server_name$request_uri; # Redirect to HTTPS
}
server {
listen 443 ssl http2;
server_name mysite.com www.mysite.com;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# HSTS — tell browsers to always use HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
root /var/www/mysite/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}