Objective. The student will integrate a responsive HTML/CSS/JavaScript frontend with the backend API, deploy the complete application to a Linux server (virtual machine or cloud instance) using a shell script and a process manager, configure UFW firewall rules and HTTPS, and verify the live deployment passes a functional and security checklist — mapping to CompTIA Linux+ exam areas: file permissions, service management, firewall configuration, and shell scripting.
This lesson, its worked examples and its practice questions has not yet been verified by an industry-certified professional or by a certification or approval body. It is a study draft and may contain errors. Do not use it as instructions for work on a job site or on live equipment. Always follow your instructor, your supervising professional, and current official references and regulations.
VoTechWorks program approval and accreditation are still in progress. Credentials are issued only by the certifying bodies named in each program, after their own exams.
This lab brings your capstone project to life on a real Linux server. You will wire your HTML/CSS/JavaScript frontend to your backend API, deploy the combined application to Ubuntu Server 22.04 LTS, lock down the server with a firewall and HTTPS, and verify that everything works correctly. Each step maps directly to CompTIA Linux+ exam areas: file permissions, service management, firewall configuration, and shell scripting.
Your frontend makes HTTP requests to API endpoints served by your backend (Python/Gunicorn or PHP-FPM). Before deployment, confirm three things in your JavaScript code:
1. Base URL — Use a relative path (`/api/...`) or an environment variable so the same code works locally and on the server without hard-coded IP addresses.
2. CORS headers — Your backend must send `Access-Control-Allow-Origin` headers that match the production domain, not a wildcard, unless the resource is genuinely public.
3. Error handling — Every `fetch()` call should have a `.catch()` block that displays a user-friendly message instead of a silent failure.
Commit and push a clean build to the `main` branch before you begin the server work. The deployment script will pull from that branch.
A deployment script automates the steps a human could forget or perform out of order. Start every Bash script with a shebang (`#!/usr/bin/env bash`) so the OS knows which interpreter to use. Immediately follow it with `set -e`, which tells Bash to exit the moment any command returns a non-zero exit code. Without `set -e`, a failed `git pull` could leave the server running stale code while the script continues happily.
The script should:
File permissions matter here. Set the script to `chmod 750` so only the owner can execute it and group members can read it, but others have no access at all. Never leave a deployment script world-readable; it may contain paths, service names, or environment references that an attacker could exploit.
UFW (Uncomplicated Firewall) is the standard tool on Ubuntu for managing `iptables` rules through a simpler interface. The principle of least privilege applies: allow only the ports your application needs.
```
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
```
Run `sudo ufw status verbose` to confirm the rules are active. Never disable port 22 before you have verified your SSH session still works — doing so locks you out of the server entirely.
For TLS, Certbot automates certificate issuance from Let's Encrypt. In lab environments without a registered domain, generate a self-signed certificate using `openssl`. Either way, configure Nginx to redirect all port-80 traffic to port 443 with a `301` redirect in the server block. Verify the redirect with:
```
curl -I http://yourdomain.com
```
You should see `HTTP/1.1 301 Moved Permanently` and a `Location:` header pointing to the HTTPS version. If you see `200 OK` over plain HTTP, the redirect is not working — correct the Nginx configuration and reload the service with `sudo systemctl reload nginx`.
Systemd controls how your application starts, stops, and restarts. A unit file (stored in `/etc/systemd/system/`) defines the service. Key fields include `ExecStart`, `Restart=on-failure`, `User`, and `WorkingDirectory`. Run `sudo systemctl daemon-reload` whenever you edit the unit file, then `sudo systemctl enable yourapp` to start it automatically at boot.
Check the service status and recent logs with:
```
sudo systemctl status yourapp
sudo journalctl -u yourapp -n 50
```
After the application is live, walk through a manual OWASP checklist. At minimum, verify:
Document every finding — passed checks and any issues you remediated — in a one-page security hardening report saved as `/docs/security-hardening-report.md` and committed to the repository.
Before calling the deployment complete, test every CRUD operation (Create, Read, Update, Delete), log in and log out, attempt an action while unauthenticated to confirm access control, and load a non-existent URL to confirm the 404 page. Record each result in the instructor's deployment checklist. All critical items must show Pass before sign-off.
When working on a remote server, always maintain at least one active SSH session while making firewall or service changes. Test new rules in a second terminal before closing the first. A misconfigured firewall rule or a failed service restart can make the server unreachable; having a second session open gives you a chance to roll back without requesting a server reset.
Answer each question, then check it. Nothing is recorded; this is your own practice.
A free tech program — complimentary, as our commitment to get America working. Help us build VoTechWorks. Tell us what works, what doesn’t, and what you want to learn.