# Apache Configuration Explained — API Documentation Server

This document explains every line and section of the Apache virtual host configuration files
used in this project. Use this as a reference to understand how the server works and how
each directive contributes to the overall setup.

---

## Table of Contents

- [Overview: How the System Works](#overview-how-the-system-works)
- [File Structure](#file-structure)
- [HTTP Virtual Host (Port 80)](#http-virtual-host-port-80)
- [SSL Virtual Host (Port 443)](#ssl-virtual-host-port-443)
- [URL Routing Map](#url-routing-map)
- [Required Apache Modules](#required-apache-modules)
- [How Each Directive Works](#how-each-directive-works)

---

## Overview: How the System Works

When a user visits `https://api.example.com`, the request flows through this chain:

```
User's Browser
     │
     ▼
Cloudflare (DNS + CDN + Edge SSL)
     │  Cloudflare terminates the user's SSL connection,
     │  then opens a NEW SSL connection to your VPS (Full Strict mode)
     ▼
Apache on VPS (Port 443)
     │  Apache uses SNI (Server Name Indication) to pick the
     │  correct VirtualHost based on the domain name
     ▼
┌─────────────────────────────────────────────────────┐
│  Apache Virtual Host for this domain                │
│                                                     │
│  /           → 301 redirect to /doc/                │
│  /doc/       → Static HTML (Redoc) from filesystem  │
│  /swagger    → Reverse proxy to Docker port 8081    │
│  /openapi/*  → Static YAML files from filesystem    │
│  /services   → Static HTML from filesystem          │
└─────────────────────────────────────────────────────┘
     │
     │  (for /swagger only)
     ▼
Docker Container (Swagger UI)
     Running on localhost:8081
     Nginx inside the container serves the Swagger UI
```

### Two Configs Per Domain

Each domain needs **two** Apache config files:

| File | Purpose |
|------|---------|
| `<domain>.conf` | **HTTP (Port 80)** — Only redirects all traffic to HTTPS |
| `<domain>-le-ssl.conf` | **HTTPS (Port 443)** — The real config that serves the application |

---

## File Structure

```
/etc/apache2/
├── sites-available/              ← All config files live here
│   ├── api.example.com.conf
│   ├── api.example.com-le-ssl.conf
│   ├── apidoc.tripfindy.com.conf
│   └── apidoc.tripfindy.com-le-ssl.conf
│
├── sites-enabled/                ← Symlinks to active configs
│   ├── api.example.com.conf → ../sites-available/...
│   ├── api.example.com-le-ssl.conf → ../sites-available/...
│   ├── apidoc.tripfindy.com.conf → ../sites-available/...
│   └── apidoc.tripfindy.com-le-ssl.conf → ../sites-available/...
│
/var/www/api-docs/                ← DocumentRoot (shared by all domains)
├── doc/                          ← Redoc HTML files
│   ├── index.html                ← Main documentation page
│   ├── generated-flight.html
│   ├── generated-hotel.html
│   └── ...
├── openapi/                      ← OpenAPI YAML spec files
│   ├── flight.yaml
│   ├── hotel.yaml
│   └── ...
├── services/
│   └── index.html                ← Services directory page
└── swagger/
    └── docker-compose.yml        ← Swagger UI Docker setup

/etc/letsencrypt/live/            ← SSL certificates (one folder per domain)
├── api.example.com/
│   ├── fullchain.pem
│   └── privkey.pem
└── apidoc.tripfindy.com/
    ├── fullchain.pem
    └── privkey.pem
```

---

## HTTP Virtual Host (Port 80)

**File:** `api.example.com.conf`

This config handles **unencrypted HTTP** requests. Its ONLY job is to redirect everything to HTTPS.

```apache
<VirtualHost *:80>
```
> **What it does:** Defines a virtual host that listens on port 80 (HTTP) for all IP addresses (`*`).
> Apache can have many `<VirtualHost>` blocks — it uses the `ServerName` to decide which one handles each request.

```apache
    ServerName api.example.com
```
> **What it does:** Tells Apache: "This virtual host handles requests for `api.example.com`".
> When a request arrives, Apache checks the `Host` header and matches it to the correct `ServerName`.
>
> **IMPORTANT:** Each domain must have its OWN virtual host file. Never use `ServerAlias` to share
> a single SSL virtual host across domains with different SSL certificates.

```apache
    DocumentRoot /var/www/api-docs
```
> **What it does:** Sets the root directory for serving files. Any URL path maps to this directory.
> For example: `http://domain.com/doc/index.html` → `/var/www/api-docs/doc/index.html`
>
> This is why `/doc/` works without needing an `Alias` — the `doc/` folder exists inside DocumentRoot.

```apache
    <Directory "/var/www/api-docs">
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>
```
> **What it does:** Sets permissions for the DocumentRoot directory:
> - `Options Indexes` — If a directory has no `index.html`, show a file listing (for debugging)
> - `Options FollowSymLinks` — Allow Apache to follow symbolic links
> - `AllowOverride None` — Ignore `.htaccess` files (better performance)
> - `Require all granted` — Allow all visitors (no IP restrictions)

```apache
    Alias /openapi /var/www/api-docs/openapi
```
> **What it does:** Maps the URL path `/openapi` to the filesystem path `/var/www/api-docs/openapi`.
> This is technically redundant here (since DocumentRoot already covers it), but it's explicit
> and ensures the path works even if DocumentRoot changes in the future.

```apache
    <Directory "/var/www/api-docs/openapi">
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        Header set Access-Control-Allow-Origin "*"
    </Directory>
```
> **What it does:** Sets permissions for the OpenAPI YAML files directory.
> - `Header set Access-Control-Allow-Origin "*"` — **CORS header**. This allows JavaScript from
>   ANY domain to fetch these YAML files. This is essential because Swagger UI (running in the
>   browser) needs to fetch the YAML specs via AJAX requests.

```apache
    ProxyPreserveHost On
    ProxyPass /swagger http://localhost:8081/
    ProxyPassReverse /swagger http://localhost:8081/
```
> **What it does:** Reverse proxy configuration for Swagger UI:
> - `ProxyPreserveHost On` — Passes the original `Host` header to the Docker container
>   (so the container knows which domain the request came from)
> - `ProxyPass /swagger http://localhost:8081/` — Any request to `/swagger` gets forwarded to
>   the Docker container running on port 8081
> - `ProxyPassReverse /swagger http://localhost:8081/` — Rewrites response headers (like
>   `Location` redirects) from the container back to use `/swagger` URLs
>
> **Example:** `https://api.example.com/swagger` → `http://localhost:8081/`

```apache
    RewriteEngine on
    RewriteCond %{SERVER_NAME} =api.example.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
```
> **What it does:** HTTP → HTTPS redirect (added by Certbot):
> - `RewriteEngine on` — Enables the URL rewriting engine
> - `RewriteCond %{SERVER_NAME} =api.example.com` — Only apply to this specific domain
> - `RewriteRule ^ https://...` — Redirect ALL requests to the HTTPS version
> - `[END]` — Stop processing further rewrite rules
> - `[NE]` — Don't escape special characters in the URL
> - `[R=permanent]` — Send a 301 Permanent Redirect status code
>
> **Result:** `http://api.example.com/anything` → `https://api.example.com/anything`

```apache
</VirtualHost>
```
> Closes the virtual host block.

---

## SSL Virtual Host (Port 443)

**File:** `api.example.com-le-ssl.conf`

This is the **main config** — it handles all HTTPS traffic and serves the actual application.

```apache
<IfModule mod_ssl.c>
```
> **What it does:** Only process this block if the SSL module (`mod_ssl`) is loaded.
> This prevents Apache from crashing if SSL is not installed.

```apache
<VirtualHost *:443>
```
> **What it does:** Defines a virtual host on port 443 (HTTPS). Apache uses **SNI**
> (Server Name Indication) to select the right virtual host — the browser sends the
> domain name during the TLS handshake, and Apache matches it to the correct `ServerName`.

```apache
    ServerName api.example.com
    DocumentRoot /var/www/api-docs
```
> Same as the HTTP config — identifies the domain and sets the file root.

```apache
    <Directory /var/www/>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>
```
> **What it does:** Security settings for the SSL version:
> - `Options -Indexes` — **Disable directory listing** (note the minus `-` sign).
>   Unlike the HTTP config, we block file listings on HTTPS to prevent information leakage.
> - `Options +FollowSymLinks` — Still allow symbolic links

```apache
    Alias /services /var/www/api-docs/services/index.html
```
> **What it does:** Maps the URL `/services` directly to a specific HTML file.
> Unlike directory aliases, this maps to a single file — so `/services` always serves
> `index.html` without needing a trailing slash.

```apache
    Alias /openapi /var/www/api-docs/openapi

    <Directory "/var/www/api-docs/openapi">
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
        Header set Access-Control-Allow-Origin "*"
    </Directory>
```
> Same as HTTP config — serves OpenAPI YAML files with CORS headers enabled.

```apache
    ProxyPreserveHost On
    ProxyPass /swagger http://localhost:8081/
    ProxyPassReverse /swagger http://localhost:8081/
```
> Same as HTTP config — reverse proxy to Docker Swagger UI container.

```apache
    ErrorLog ${APACHE_LOG_DIR}/api_docs_error.log
    CustomLog ${APACHE_LOG_DIR}/api_docs_access.log combined
```
> **What it does:** Configures logging for this virtual host:
> - `ErrorLog` — Where to write error messages (PHP errors, 404s, permission issues, etc.)
> - `CustomLog` — Where to write access logs (every request, with IP, URL, status, user agent)
> - `${APACHE_LOG_DIR}` expands to `/var/log/apache2/`
> - `combined` — A log format that includes referrer and user agent info
>
> **Each domain should have separate log files** to make debugging easier.

```apache
    Include /etc/letsencrypt/options-ssl-apache.conf
```
> **What it does:** Loads SSL security settings managed by Certbot/Let's Encrypt.
> This file contains TLS protocol versions, cipher suites, and security headers.
> Certbot keeps this file updated with current best practices — **don't edit it manually**.

```apache
    RedirectMatch 301 ^/$ /doc/
```
> **What it does:** When someone visits the root URL (`/`), redirect them to `/doc/`.
> - `301` — Permanent redirect (browsers cache this)
> - `^/$` — Regex that matches ONLY the exact root path `/`
> - `/doc/` — The destination URL (Redoc documentation index page)
>
> **How /doc/ works:** Since `DocumentRoot` is `/var/www/api-docs`, the URL `/doc/` maps to
> the filesystem path `/var/www/api-docs/doc/`. Apache finds `index.html` in that directory
> and serves it automatically. **No `Alias` directive is needed.**

```apache
    SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
```
> **What it does:** Points to the SSL certificate and private key for this domain:
> - `SSLCertificateFile` — The full certificate chain (your cert + intermediate CA certs)
> - `SSLCertificateKeyFile` — The private key that proves you own the certificate
>
> **CRITICAL RULE:** These paths MUST match the domain in `ServerName`.
> Using another domain's certificate here causes Cloudflare **Error 526 (Invalid SSL Certificate)**.
>
> These files are managed by Let's Encrypt/Certbot and auto-renew every 90 days.

```apache
</VirtualHost>
</IfModule>
```
> Closes the virtual host and the `IfModule` conditional block.

---

## URL Routing Map

Here's a complete map of every URL and where it goes:

| URL Path | What Serves It | Filesystem / Target | Notes |
|----------|---------------|--------------------|----|
| `/` | `RedirectMatch` | → `301` redirect to `/doc/` | Permanent redirect |
| `/doc/` | DocumentRoot | `/var/www/api-docs/doc/index.html` | Main Redoc page (auto serves `index.html`) |
| `/doc/generated-flight.html` | DocumentRoot | `/var/www/api-docs/doc/generated-flight.html` | Individual API doc pages |
| `/swagger` | `ProxyPass` | `http://localhost:8081/` | Docker container (Swagger UI) |
| `/swagger/anything` | `ProxyPass` | `http://localhost:8081/anything` | All sub-paths proxied too |
| `/openapi/flight.yaml` | `Alias` + Directory | `/var/www/api-docs/openapi/flight.yaml` | OpenAPI spec files (with CORS) |
| `/services` | `Alias` | `/var/www/api-docs/services/index.html` | Services directory page |

---

## Required Apache Modules

These modules must be enabled for the configuration to work:

| Module | Purpose | Enable Command |
|--------|---------|---------------|
| `mod_ssl` | HTTPS/TLS support | `sudo a2enmod ssl` |
| `mod_proxy` | Reverse proxy support | `sudo a2enmod proxy` |
| `mod_proxy_http` | HTTP protocol for proxy | `sudo a2enmod proxy_http` |
| `mod_rewrite` | URL rewriting (HTTP→HTTPS redirect) | `sudo a2enmod rewrite` |
| `mod_headers` | Custom HTTP headers (CORS) | `sudo a2enmod headers` |

Check which modules are currently active:
```bash
apache2ctl -M 2>/dev/null | grep -E "ssl|proxy|rewrite|headers"
```

---

## How Each Directive Works

### Alias vs DocumentRoot

```
DocumentRoot /var/www/api-docs
```
With DocumentRoot, URLs map directly to filesystem paths:
- `/doc/index.html` → `/var/www/api-docs/doc/index.html`
- `/openapi/flight.yaml` → `/var/www/api-docs/openapi/flight.yaml`

```
Alias /services /var/www/api-docs/services/index.html
```
An Alias overrides the DocumentRoot mapping for a specific URL path. Use it when:
- You want a URL to point to a **specific file** (not a directory)
- You want a URL to point to a location **outside** DocumentRoot

**When to NOT use Alias:** If the directory already exists under DocumentRoot
(like `/doc/`), don't use Alias — it's unnecessary and can cause confusion.

### ProxyPass vs Alias

| | ProxyPass | Alias |
|---|---|---|
| **Use for** | Dynamic apps, Docker containers | Static files on disk |
| **How it works** | Forwards the HTTP request to another server | Maps URL to filesystem path |
| **Example** | `/swagger → localhost:8081` | `/openapi → /var/www/.../openapi` |

### RedirectMatch vs RewriteRule

Both can redirect URLs, but they work differently:

| | RedirectMatch | RewriteRule |
|---|---|---|
| **Syntax** | Simple, uses regex on URL path | Complex, supports conditions |
| **Use for** | Simple one-to-one redirects | Conditional redirects (e.g., check domain) |
| **Example** | `RedirectMatch 301 ^/$ /doc/` | `RewriteRule ^ https://... [R=permanent]` |

In our config:
- `RedirectMatch` redirects `/` → `/doc/` (simple, one URL)
- `RewriteRule` redirects HTTP → HTTPS (needs `RewriteCond` to check the domain name)

---

## Common Mistakes to Avoid

| Mistake | What Happens | How to Avoid |
|---------|-------------|-------------|
| Using `ServerAlias` for domains with different SSL certs | Cloudflare Error 526 | Always create separate virtual host files per domain |
| Using `Alias /docs` when the real path is `/doc/` | URL shows `/docs` instead of `/doc/` | Let DocumentRoot handle paths that map naturally |
| Forgetting to reload Apache after changes | Changes don't take effect | Always run `sudo systemctl reload apache2` |
| Using `restart` instead of `reload` | Brief downtime for all sites | Use `reload` for zero-downtime config updates |
| Editing files in `sites-enabled/` directly | Changes may be lost | Edit in `sites-available/`, use `a2ensite` |
| Sharing one certificate across multiple domains | SSL mismatch errors | One certificate per domain (or use a SAN cert) |
| `301 Permanent` redirects | Browser caches forever | Clear browser cache + Cloudflare cache after changes |
