Hosting a static website using Apache on a VPS server
DevOpsUpdated: 12 min read

By Akshay Singh

Share this article:

How to Host a Static Website on a VPS Using Apache

Hosting your own static website on a VPS gives you complete control over your infrastructure — no platform limitations, no hidden usage caps, no vendor lock-in. You decide how your server runs, what software it uses, and how it scales.

In this guide, I'll walk you through the entire process: from connecting to a fresh Linux VPS to serving a static HTML/CSS/JS website with Apache, securing it with a free SSL certificate, configuring your firewall, and optimizing it for production.

By the end, you'll have a fully working, HTTPS-secured website running on your own server.


Why Host a Static Site on a VPS?

Most static sites can be deployed on platforms like Netlify, Vercel, or GitHub Pages with zero configuration. So why would you choose a VPS?

  • Complete control — you own the server, the configuration, and the deployment pipeline. No platform dependency.
  • No usage limits — no bandwidth caps, build minute limits, or deployment restrictions.
  • Run additional services — host multiple websites, run cron jobs, databases, APIs, or monitoring tools on the same server.
  • Learning opportunity — understanding server administration makes you a better developer. When something breaks in production, you'll know how to debug it.
  • Cost-effective at scale — a $5/month VPS from DigitalOcean, Hetzner, or Linode can serve millions of static page views per month.

Why Apache?

Apache has been serving websites since 1995. It's not the newest tool (Nginx is more popular for new setups), but it remains a solid choice:

  • Rock-solid stability — battle-tested across decades and millions of servers
  • Extensive documentation — every configuration option is well documented
  • .htaccess support — per-directory configuration without restarting the server
  • Module ecosystem — mod_rewrite, mod_ssl, mod_headers, mod_deflate, and more
  • Wide hosting compatibility — most hosting tutorials, Stack Overflow answers, and shared hosting providers use Apache

Prerequisites

Before you begin, make sure you have:

  • A Linux VPS — Ubuntu 22.04 LTS or 24.04 LTS recommended. Any provider works: DigitalOcean, Hetzner, Linode, Vultr, AWS Lightsail.
  • SSH access — you should be able to connect to your server via terminal.
  • A static website — plain HTML/CSS/JS files, or a build output from frameworks like Astro, Hugo, Vite, or Next.js (next export).
  • A domain name (optional but highly recommended) — you can buy one from Namecheap, Cloudflare, or Google Domains.

Step 1: Connect to Your VPS

Open your terminal and connect via SSH:

ssh root@your-server-ip

If you set up a non-root user during provisioning (recommended for security):

ssh your-username@your-server-ip

First time connecting? You'll see a fingerprint confirmation prompt. Type yes to continue. This only happens on the first connection.

Once connected, update your package list to make sure everything is current:

sudo apt update && sudo apt upgrade -y

This ensures you're installing the latest versions of everything and that security patches are applied.


Step 2: Install Apache

Install the Apache web server:

sudo apt install apache2 -y

Enable Apache to start automatically on server boot, then start the service:

sudo systemctl enable apache2
sudo systemctl start apache2

Verify it's running:

sudo systemctl status apache2

You should see active (running) in the output. You can also visit http://your-server-ip in your browser — you should see the Apache default page ("It works!").

If you don't see the page, Apache might be blocked by a firewall. We'll fix that in the firewall section below.


Step 3: Create Your Website Directory

Apache serves files from /var/www/ by default. Create a dedicated directory for your website:

sudo mkdir -p /var/www/mywebsite

Replace mywebsite with your actual site name (e.g., thedailydevs, portfolio, docs).

For now, create a test HTML file to confirm everything works:

sudo nano /var/www/mywebsite/index.html

Paste this minimal HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>
</head>
<body>
    <h1>It works! My website is live.</h1>
    <p>Served by Apache on my own VPS.</p>
</body>
</html>

Save and exit (Ctrl+X, then Y, then Enter in nano).


Step 4: Configure a Virtual Host

Virtual hosts let Apache serve multiple websites from a single server. Even if you only have one site, using a virtual host is the proper way to configure Apache.

Create a new configuration file:

sudo nano /etc/apache2/sites-available/mywebsite.conf

Paste the following configuration:

<VirtualHost *:80>
    ServerAdmin webmaster@yourdomain.com
    ServerName yourdomain.com
    ServerAlias www.yourdomain.com
    DocumentRoot /var/www/mywebsite

    <Directory /var/www/mywebsite>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Security headers
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    ErrorLog ${APACHE_LOG_DIR}/mywebsite-error.log
    CustomLog ${APACHE_LOG_DIR}/mywebsite-access.log combined
</VirtualHost>

Important configuration notes:

  • ServerName — your primary domain (e.g., thedailydevs.com). If you don't have a domain yet, use your server's IP address.
  • ServerAlias — additional domains that should serve the same site (e.g., www.yourdomain.com).
  • Options -Indexesprevents directory listing. Without this, visitors could browse your file structure if you're missing an index.html in a subdirectory. This is a security risk.
  • AllowOverride All — allows .htaccess files to work in your website directory.
  • ErrorLog / CustomLog — separate log files per site make debugging much easier.
  • Security headers — basic protections against common attacks (MIME sniffing, clickjacking).

Enable the headers module (needed for the security headers above), enable your site, and disable the default site:

sudo a2enmod headers
sudo a2ensite mywebsite.conf
sudo a2dissite 000-default.conf

Test the configuration for syntax errors:

sudo apache2ctl configtest

You should see Syntax OK. If there are errors, fix them before proceeding.

Reload Apache to apply the changes:

sudo systemctl reload apache2

Visit http://your-server-ip — you should see your "It works!" test page.


Step 5: Upload Your Static Website

Now replace the test page with your actual website files. You have several options:

Option A: SCP (from your local machine)

# Upload all files from your local build directory
scp -r ./dist/* your-username@your-server-ip:/var/www/mywebsite/

For framework build outputs:

  • Vite: ./dist/*
  • Next.js static export: ./out/*
  • Astro: ./dist/*
  • Hugo: ./public/*
  • Plain HTML: ./your-folder/*

Option B: rsync (better for updates)

rsync only transfers files that have changed, making subsequent deployments much faster:

# First deployment
rsync -avz --progress ./dist/ your-username@your-server-ip:/var/www/mywebsite/

# Subsequent deployments (deletes old files not in source)
rsync -avz --delete --progress ./dist/ your-username@your-server-ip:/var/www/mywebsite/

Option C: Git-based deployment

Clone your repo directly on the server:

cd /var/www/mywebsite
sudo git clone https://github.com/yourusername/yoursite.git .

For updates, just pull:

cd /var/www/mywebsite && sudo git pull

Option D: SFTP (GUI tool)

Use FileZilla, Cyberduck, or VS Code's Remote SSH extension to drag and drop files visually.


Step 6: Set Correct File Permissions

Apache runs as the www-data user. Your website files need to be readable by this user:

# Set ownership to www-data
sudo chown -R www-data:www-data /var/www/mywebsite

# Set directory permissions (755 = owner rwx, group/others rx)
sudo find /var/www/mywebsite -type d -exec chmod 755 {} \;

# Set file permissions (644 = owner rw, group/others r)
sudo find /var/www/mywebsite -type f -exec chmod 644 {} \;

Why these specific permissions?

  • 755 on directories — Apache can enter and list the directory
  • 644 on files — Apache can read the files but not modify them
  • www-data ownership — Apache's user can access everything

If you skip this step, you'll likely see a 403 Forbidden error.


Step 7: Configure Your Domain's DNS

If you have a domain name, point it to your VPS:

  1. Log into your domain registrar (Namecheap, Cloudflare, GoDaddy, etc.)
  2. Go to DNS settings
  3. Add or update these records:
TypeHostValueTTL
A@your-server-ip3600
Awwwyour-server-ip3600

DNS propagation typically takes 5-30 minutes, but can take up to 48 hours.

Verify DNS is working:

# From your local machine
dig yourdomain.com +short
# Should return your server IP

nslookup yourdomain.com
# Should show your server IP

Once DNS propagates, update your Apache config's ServerName if you were using the IP address, and reload:

sudo systemctl reload apache2

Step 8: Enable the Firewall

Ubuntu comes with UFW (Uncomplicated Firewall). Enable it and allow the necessary traffic:

# Allow SSH (important! Don't lock yourself out)
sudo ufw allow OpenSSH

# Allow HTTP and HTTPS traffic
sudo ufw allow 'Apache Full'

# Enable the firewall
sudo ufw enable

# Verify the rules
sudo ufw status

You should see:

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Apache Full                ALLOW       Anywhere

Warning: Always allow SSH before enabling UFW. If you enable the firewall without allowing SSH, you'll lock yourself out of the server.


Step 9: Enable HTTPS with Let's Encrypt

HTTPS is no longer optional. Browsers mark HTTP sites as "Not Secure," and Google uses HTTPS as a ranking signal. Let's Encrypt provides free SSL certificates.

Install Certbot (the Let's Encrypt client for Apache):

sudo apt install certbot python3-certbot-apache -y

Run Certbot to obtain and install the certificate:

sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

Certbot will:

  1. Verify you own the domain
  2. Obtain an SSL certificate
  3. Automatically modify your Apache config to use HTTPS
  4. Set up HTTP → HTTPS redirect

Verify auto-renewal works:

sudo certbot renew --dry-run

Let's Encrypt certificates expire every 90 days. Certbot automatically sets up a cron job to renew them. The --dry-run flag tests that the renewal process works without actually renewing.

Visit https://yourdomain.com — you should see your site with a padlock icon.


Step 10: Performance Optimization

A few Apache modules can significantly improve your site's load time.

Enable Gzip Compression

Compress text-based files (HTML, CSS, JS, JSON, SVG) before sending them to the browser:

sudo a2enmod deflate
sudo systemctl reload apache2

Create or edit .htaccess in your website root:

sudo nano /var/www/mywebsite/.htaccess

Add:

# Enable gzip compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css
    AddOutputFilterByType DEFLATE text/javascript application/javascript
    AddOutputFilterByType DEFLATE application/json application/xml
    AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>

Enable Browser Caching

Tell browsers to cache static assets so returning visitors load your site faster:

# Add to .htaccess
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/html "access plus 1 hour"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 month"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

Enable the expires module:

sudo a2enmod expires
sudo systemctl reload apache2

Enable mod_rewrite

If you need clean URLs or custom redirects:

sudo a2enmod rewrite
sudo systemctl reload apache2

Troubleshooting Common Issues

403 Forbidden

Cause: Apache can't read your files.

# Fix permissions
sudo chown -R www-data:www-data /var/www/mywebsite
sudo find /var/www/mywebsite -type d -exec chmod 755 {} \;
sudo find /var/www/mywebsite -type f -exec chmod 644 {} \;

# Check if Options -Indexes is causing issues when index.html is missing
ls -la /var/www/mywebsite/

500 Internal Server Error

Cause: Usually a bad .htaccess file or misconfigured module.

# Check the error log
sudo tail -20 /var/log/apache2/mywebsite-error.log

Site not loading after DNS change

Cause: DNS hasn't propagated yet, or Apache config doesn't match the domain.

# Check if DNS has propagated
dig yourdomain.com +short

# Test Apache config
sudo apache2ctl configtest

# Verify the correct site is enabled
sudo apache2ctl -S

Certbot fails to obtain certificate

Cause: DNS not pointing to your server, or port 80 is blocked.

# Verify port 80 is open
sudo ufw status
sudo ufw allow 'Apache Full'

# Verify DNS points to this server
dig yourdomain.com +short
# Should return THIS server's IP

Changes not appearing after upload

Cause: Browser cache or Apache cache.

# Hard refresh in browser: Ctrl+Shift+R (or Cmd+Shift+R on Mac)

# Or clear Apache's cache if mod_cache is enabled
sudo systemctl restart apache2

Hosting Multiple Sites on One Server

One of the biggest advantages of a VPS is hosting multiple websites on the same server. Each site gets its own virtual host:

# Create directories
sudo mkdir -p /var/www/site1
sudo mkdir -p /var/www/site2

# Create separate virtual host configs
sudo nano /etc/apache2/sites-available/site1.conf
sudo nano /etc/apache2/sites-available/site2.conf

# Enable both sites
sudo a2ensite site1.conf
sudo a2ensite site2.conf
sudo systemctl reload apache2

# Get SSL for both
sudo certbot --apache -d site1.com -d www.site1.com
sudo certbot --apache -d site2.com -d www.site2.com

Apache uses the ServerName directive to route incoming requests to the correct virtual host.


Quick Reference: Useful Commands

CommandPurpose
sudo systemctl status apache2Check if Apache is running
sudo systemctl restart apache2Restart Apache
sudo systemctl reload apache2Reload config without downtime
sudo apache2ctl configtestTest config for syntax errors
sudo apache2ctl -SList all virtual hosts
sudo a2ensite mysite.confEnable a virtual host
sudo a2dissite mysite.confDisable a virtual host
sudo a2enmod rewriteEnable a module
sudo tail -f /var/log/apache2/error.logWatch error log in real-time
sudo certbot renew --dry-runTest SSL renewal

Final Thoughts

Self-hosting a static website on a VPS isn't as intimidating as it sounds. The entire process — from a fresh Ubuntu server to an HTTPS-secured, performance-optimized website — takes about 20 minutes once you know the steps.

The key takeaways:

  1. Always use a virtual host — even for a single site, it keeps your config clean and makes adding more sites easy
  2. Set proper file permissions — most "403 Forbidden" errors come from wrong ownership or permissions
  3. Enable HTTPS from day one — it's free with Let's Encrypt and takes 2 minutes
  4. Enable compression and caching — they make a huge difference in load time with almost zero effort
  5. Keep your server updated — run sudo apt update && sudo apt upgrade regularly

If you get stuck somewhere, please reach out via the Contact Us page.


Keep Learning

Happy deploying!

apachevpsweb hostingstatic sitelinux
TheDailyDevsTheDailyDevs
TheDailyDevs is a developer-first blog and knowledge hub created by passionate engineers to share real-world development tips, deep-dive tutorials, industry insights, and hands-on solutions to everyday coding challenges. Whether you're building apps, exploring new frameworks, or leveling up your dev game, you'll find practical, no-fluff content here, updated daily.