Hosting & Web Security

A structured guide for deploying and securing web applications

Author: tworjaga | Telegram: @al7exy

WARNING: Use at your own risk. This guide provides educational content for securing systems. Always test configurations in isolated environments before production deployment. The authors assume no liability for damages or security incidents resulting from the use of this material.

1. Project Goal

Build a structured, technically correct guide that teaches:

Tone: Clear, practical, no fluff, no marketing language. Focus on real-world habits.

2. Hosting Fundamentals

What is Hosting

Hosting provides the infrastructure (servers, storage, network) to make websites accessible on the internet. A server is a computer that runs continuously, serving content to visitors.

Types of Hosting

Type Description Use Case Control Level
Shared Hosting Multiple websites on one server Static sites, blogs Minimal
VPS (Virtual Private Server) Virtualized server with dedicated resources Web apps, APIs Full root access
Dedicated Server Physical server exclusively yours High-traffic applications Complete
Serverless Cloud-managed functions, no server management Event-driven apps None (managed)

Domain and DNS

A domain is a human-readable address (example.com). DNS (Domain Name System) translates domains to IP addresses.

DNS Resolution Flow: User types: example.com | v DNS Resolver (ISP/8.8.8.8) | v Root DNS Server -> TLD Server (.com) -> Authoritative Server | v Returns IP: 192.0.2.1 | v Browser connects to IP

IP Addresses

IPv4: 32-bit address (192.168.1.1). IPv6: 128-bit address (2001:0db8::1). Public IPs are routable on the internet; private IPs are for local networks.

Reverse Proxy

A server that sits between clients and backend servers. Handles SSL termination, load balancing, and caching. Common implementations: Nginx, Caddy, Traefik.

Client -> Reverse Proxy (Nginx) -> Application Server (Node.js/Python) | v Static Files (Cache)

SSL/TLS

Transport Layer Security encrypts data between client and server. TLS 1.3 is current standard. Certificates are issued by Certificate Authorities (Let's Encrypt, free).

3. Beginner Deployment Paths

Path 1: GitHub Pages (Static Sites)

Free hosting for static websites directly from GitHub repositories.

Setup Steps

  1. Create repository: username.github.io
  2. Push HTML/CSS/JS files to main branch
  3. Enable Pages in repository settings

Folder Structure

username.github.io/
├── index.html
├── css/
│   └── style.css
├── js/
│   └── app.js
└── assets/
    └── images/

Common Mistakes

Path 2: Vercel / Netlify

Platform-as-a-Service for frontend frameworks and static sites.

Vercel Deployment

# Install CLI
npm i -g vercel

# Login
vercel login

# Deploy
vercel --prod

Netlify Deployment

# Install CLI
npm install -g netlify-cli

# Login
netlify login

# Deploy
netlify deploy --prod --dir=build

Troubleshooting

Path 3: Basic VPS Deployment

Deploying to a virtual private server provides full control.

Prerequisites

Connection

# Default connection (password auth - temporary)
ssh root@your_server_ip

# Verify host key fingerprint matches provider dashboard

4. VPS Production Setup Guide

Step 1: Initial Server Access

# Connect as root (only for initial setup)
ssh root@server_ip

# Update system packages
apt update && apt upgrade -y

# Install essential tools
apt install -y curl wget vim ufw fail2ban

Step 2: Create Non-Root User

# Create user
adduser deploy

# Add to sudo group
usermod -aG sudo deploy

# Switch to new user
su - deploy

# Verify sudo access
sudo whoami

Step 3: SSH Key Authentication

On local machine:

# Generate SSH key pair (if not exists)
ssh-keygen -t ed25519 -C "[email protected]"

# Copy public key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@server_ip

Step 4: Secure SSH Configuration

# Edit SSH config
sudo nano /etc/ssh/sshd_config

# Modify these lines:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3

# Restart SSH
sudo systemctl restart sshd

Step 5: Configure Firewall (UFW)

# Default deny
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (before enabling)
sudo ufw allow 22/tcp

# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Check status
sudo ufw status verbose

Step 6: Install Docker

# Remove old versions
sudo apt remove docker docker-engine docker.io

# Install dependencies
sudo apt install -y apt-transport-https ca-certificates gnupg lsb-release

# Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Add repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io

# Add user to docker group
sudo usermod -aG docker deploy

# Verify
docker --version

Step 7: Application Deployment

Create application structure:

mkdir -p ~/app
cd ~/app

# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
EOF

Step 8: Reverse Proxy with Caddy

# Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

# Configure Caddyfile
sudo nano /etc/caddy/Caddyfile
example.com {
    reverse_proxy localhost:3000
    encode gzip
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        X-XSS-Protection "1; mode=block"
    }
}
# Reload Caddy
sudo systemctl reload caddy

# Enable auto-start
sudo systemctl enable caddy

Step 9: SSL with Let's Encrypt (Caddy handles automatically)

Caddy automatically provisions and renews certificates. For manual certbot with Nginx:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

# Auto-renewal test
sudo certbot renew --dry-run

Step 10: Process Management

Create systemd service for application:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/home/deploy/app
ExecStart=/usr/bin/docker run --rm -p 3000:3000 myapp:latest
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# Check logs
sudo journalctl -u myapp -f

Step 11: Backup Strategy

# Install restic for backups
sudo apt install restic

# Initialize backup repository
restic init --repo /backup/repo

# Create backup script
cat > ~/backup.sh << 'EOF'
#!/bin/bash
export RESTIC_PASSWORD="your-secure-password"
restic -r /backup/repo backup /home/deploy/app/data
restic -r /backup/repo forget --keep-last 7 --keep-daily 30
EOF

chmod +x ~/backup.sh

# Add to crontab (daily at 2 AM)
0 2 * * * /home/deploy/backup.sh

5. Security Practices

Never Do This

Always Do This

Fail2ban Configuration

sudo apt install fail2ban

sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
backend = systemd

[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo fail2ban-client status sshd

Automatic Updates

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

6. How You Get Hacked

SSH Brute Force

What: Automated attempts to guess SSH credentials

How: Bots scan internet for port 22, try common passwords

Prevention: Disable password auth, use keys only, move SSH to non-standard port, deploy Fail2ban

Exposed MongoDB/Redis

What: Database accessible without authentication

How: Default configurations bind to 0.0.0.0 without password

Prevention: Bind to 127.0.0.1 only, enable auth, firewall block port 27017/6379

SQL Injection

What: Malicious SQL commands through user input

How: Unsanitized input concatenated into queries

Prevention: Use parameterized queries/prepared statements, ORM frameworks, input validation

Cross-Site Scripting (XSS)

What: Injection of client-side scripts into web pages

How: User input displayed without encoding

Prevention: Output encoding, Content Security Policy, HttpOnly cookies

Cross-Site Request Forgery (CSRF)

What: Unauthorized actions performed on authenticated session

How: Malicious site triggers requests to target site

Prevention: CSRF tokens, SameSite cookies, validate Origin header

Supply Chain Attacks

What: Compromise through dependencies

How: Malicious npm packages, compromised build tools

Prevention: Lock dependencies (package-lock.json), audit with npm audit, private registry

Misconfigured CORS

What: Overly permissive cross-origin resource sharing

How: Access-Control-Allow-Origin: * on sensitive endpoints

Prevention: Whitelist specific origins, validate origin server-side

Docker Misconfiguration

What: Container escape or privilege escalation

How: Running containers as root, mounting Docker socket

Prevention: Run as non-root user, read-only filesystems, drop capabilities

.env Leaks

What: Exposure of environment variables

How: Committed to git, exposed in error messages

Prevention: .gitignore .env, validate no secrets in repo with git-secrets, use secret management

Open S3 Buckets

What: Publicly accessible cloud storage

How: Default public permissions, misconfigured bucket policies

Prevention: Block public access settings, IAM policies, regular audits

7. CI/CD & DevOps Basics

GitHub Actions Deployment

Create .github/workflows/deploy.yml:

name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run tests
        run: npm test
        
      - name: Build
        run: npm run build
        
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /home/deploy/app
            git pull origin main
            docker build -t myapp:latest .
            docker stop myapp || true
            docker rm myapp || true
            docker run -d --name myapp -p 3000:3000 myapp:latest

Secret Management

Environment Variables

# Production .env file (not committed)
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@localhost/db
JWT_SECRET=use-random-256-bit-string-here
API_KEY=external-service-key

Rate Limiting

Implement at reverse proxy or application level:

# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=one burst=20 nodelay;
    }
}

Logging

# Centralized logging with journald
sudo journalctl -u myapp -f -n 100

# Structured logging format (JSON)
{"timestamp":"2024-01-01T00:00:00Z","level":"error","message":"DB connection failed","request_id":"uuid"}

Monitoring Concepts

Cloudflare Basics

# DNS configuration
Type: A
Name: example.com
Content: your_server_ip
Proxy status: Proxied (orange cloud)

# Always Use HTTPS: On
# Security Level: Medium
# Browser Cache TTL: 4 hours

8. Production Checklist

Server Security

Application Security

Data Protection

Monitoring & Maintenance

9. Learning Paths

Beginner Safe Path

  1. Start with GitHub Pages for static sites
  2. Learn DNS basics with custom domain
  3. Progress to Netlify/Vercel with CI/CD
  4. Study Linux fundamentals
  5. Practice with local Docker containers
  6. Deploy first VPS with manual setup
  7. Implement basic security hardening

Production Safe Path

  1. Infrastructure as Code (Terraform/Ansible)
  2. Container orchestration (Docker Compose/Kubernetes)
  3. CI/CD pipeline with automated testing
  4. Centralized logging and monitoring
  5. Automated backup and disaster recovery
  6. Security scanning in pipeline
  7. Blue-green or canary deployments

10. Common Beginner Mistakes

Configuration Errors

Security Oversights

Operational Errors

Development Practices

11. Additional Attack Vectors

Server-Side Request Forgery (SSRF)

What: Attacker tricks the server into making HTTP requests to internal resources

How: URL parameters passed to server-side HTTP clients without validation (e.g. fetch(req.body.url))

Prevention: Whitelist allowed domains, block private IP ranges (10.x, 172.16.x, 192.168.x, 169.254.x), use a dedicated egress proxy

Path Traversal

What: Access to files outside the intended directory by manipulating paths

How: Input like ../../etc/passwd passed to file read functions

Prevention: Resolve and validate paths against allowed base directory, use path.resolve() + startsWith check, never expose raw user input to filesystem calls

Clickjacking

What: Attacker overlays invisible iframe to trick users into clicking hidden elements

How: Your page embedded in malicious site's iframe

Prevention: Set X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none'

Missing Multi-Factor Authentication

What: Account takeover via credential stuffing or phishing even with strong passwords

How: Leaked credential databases reused across services (credential stuffing)

Prevention: Enforce TOTP/WebAuthn for all admin access; use speakeasy or otplib for TOTP; consider passkeys (FIDO2) for users

Hardcoded Secrets in Container Images

What: Secrets baked into Docker image layers remain accessible even after deletion

How: COPY .env or RUN commands with credentials in Dockerfile leave traces in image history

Prevention: Use --secret BuildKit mounts, inject secrets at runtime via environment variables or secrets managers (HashiCorp Vault, AWS Secrets Manager, Doppler)

12. Secrets Management

Never store secrets in code or Docker images. Use a dedicated secrets manager:

Tools

Runtime Injection Pattern

# Docker: inject at runtime, never baked in
docker run -e DATABASE_URL="$DATABASE_URL" -e JWT_SECRET="$JWT_SECRET" myapp:latest

# Or use Docker secrets (Swarm/Compose)
echo "my_secret_value" | docker secret create db_password -

# .gitignore — always include:
.env
.env.local
.env.production
*.key
*.pem

Scan for Leaked Secrets

# Install trufflehog (scans git history)
pip install trufflehog
trufflehog git file://. --only-verified

# Or gitleaks
gitleaks detect --source . -v

# Pre-commit hook with detect-secrets
pip install detect-secrets
detect-secrets scan > .secrets.baseline
# Add as pre-commit hook to prevent future commits

13. Content Security Policy (CSP)

CSP is one of the most powerful defenses against XSS. Many guides skip it — don't.

Basics

# Nginx — add to server block
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;

# Report-only mode (test without breaking things)
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report;" always;

Key Directives

Directive Purpose
default-src 'self'Baseline: only load resources from same origin
script-srcWhere JavaScript can be loaded from
frame-ancestors 'none'Prevents your page being embedded (clickjacking)
upgrade-insecure-requestsForces HTTP resources to be requested as HTTPS
report-uri /endpointBrowser reports violations here — use in staging

⚠ Warning: Never use unsafe-inline or unsafe-eval in script-src — this defeats the purpose of CSP entirely. Use nonces instead.