Last updated: 2026-05-27

This document is the plain-English overview of the CFTS reverse proxy. Start here if you are new to the setup, especially if you need to understand what Caddy is doing or read the logs without being deep into Linux.

## What This Server Does

`rproxy.cfts.co` is the front door for many CFTS web services.

People on the internet or LAN visit names such as:

```text
docs.cfts.co
tickets.cfts.co
inventory.cfts.co
rp-logs.cfts.co
```

Those requests arrive at Caddy on `rproxy.cfts.co`. Caddy then decides what to do:

- serve HTTPS certificates
- redirect HTTP to HTTPS for known hostnames
- block unknown HTTP hostnames
- allow or deny access based on source IP
- add security headers where safe
- show shared Caddy error pages for Caddy-owned `403`, `404`, and `5xx` errors
- proxy allowed traffic to the correct internal server
- write access logs

In simple terms: Caddy is the gatekeeper and traffic router. Most applications still do their own login and app logic behind it.

## Traffic Flow

```mermaid
flowchart LR
    User["User browser"] --> DNS["DNS name, for example docs.cfts.co"]
    DNS --> Caddy["Caddy on rproxy.cfts.co"]
    Caddy --> Decision["Caddy checks hostname, HTTPS, LAN rules, headers"]
    Decision --> App["Internal app server"]
    Decision --> Deny["Caddy 403 page"]
    Caddy --> Logs["Caddy JSON logs"]
    Logs --> Report["GoAccess report at rp-logs.cfts.co"]
```

If the request is allowed, Caddy passes it to an internal app such as `172.16.198.22:8090`.

If the request is not allowed, Caddy can stop it before it reaches the app.

## Important Docs

Use these in this order:

| Document | Use it for |
| --- | --- |
| `Project-Documentation/00. rproxy Caddy Overview For New Admins.md` | Plain-English orientation. |
| `Project-Documentation/03. rproxy Function and Feature Synopsis.md` | Shared baseline for future user and technical documentation. |
| `Project-Documentation/02. rproxy Exposure Matrix.md` | Which hostname points where, whether it is public or LAN-only, and what log file it uses. |
| `Project-Documentation/01. Caddy Reverse Proxy Operations.md` | Operational commands, deployment steps, and deeper maintenance notes. |
| `observability/README.md` | Paste-safe log dashboard install/update runbook. |

## Access Patterns

There are three common access patterns.

| Pattern | What it means | Examples |
| --- | --- | --- |
| Public | Anyone can reach the Caddy route. The upstream app may still require login. | `docs.cfts.co`, `tickets.cfts.co`, `redmine.cfts.co`, `tracks.cfts.co` |
| LAN-only | The DNS name exists publicly, but Caddy only allows clients from `172.16.198.0/24`. Other clients get `403`. | `download.cfts.co`, `inventory.cfts.co`, `rp-logs.cfts.co`, `ai.cfts.co` |
| Mixed | Some paths are public, some are LAN-only or protected by Caddy Basic Auth. | `isp-status.cfts.co` |

Do not assume a public DNS name means the service is open to the public. Several names are intentionally LAN-only.

## What The Error Pages Mean

Caddy has shared custom pages for Caddy-owned errors:

| Status | Plain meaning | Common cause |
| --- | --- | --- |
| `403` | Caddy understood the request but blocked access. | Client is outside the LAN for a LAN-only site. |
| `404` | Caddy did not find a matching Caddy-owned route or static file. | Wrong URL on a Caddy-served static site. |
| `500` | Caddy hit a server-side problem. | Caddy-side error or upstream connection problem, depending on the route. |

Important: if an upstream app returns its own `404` or `500`, Caddy usually passes that through unchanged. For example, a docs app `404` is normally the docs app talking, not the shared Caddy `404` page.

## Reading Logs Without Linux

Use the dashboard first:

```text
https://rp-logs.cfts.co/
```

This is LAN-only. If you open it from outside the LAN, Caddy should show `403`.

The dashboard is generated by GoAccess every five minutes from Caddy JSON logs. It is a static HTML report, not a separate always-running web app.

Good first things to look at:

| Dashboard area | What it tells you |
| --- | --- |
| Total Requests | Overall traffic volume in the selected date range. |
| Failed Requests | Non-success responses. Useful as a quick health smell test. |
| Not Found | Number of `404` responses. A sudden jump can mean broken links or scanners. |
| Requested Files / URLs | Most-requested paths. Useful for seeing what people and bots are hitting. |
| Static Requests | CSS, JS, images, icons, and other static files. |
| Not Found URLs | Which missing paths are being requested. Great for spotting `/wp-login.php`, `/.env`, and other scanner noise. |
| Hosts / Visitors | Which clients are making requests. Use carefully; NAT and proxies can group many users behind one IP. |
| Status Codes | The mix of `200`, `301/308`, `401`, `403`, `404`, and `5xx`. |

## Status Code Cheat Sheet

| Code | Meaning | Usually OK? |
| --- | --- | --- |
| `200` | Request succeeded. | Yes. |
| `301` / `308` | Redirect, usually HTTP to HTTPS. | Yes, if expected. |
| `401` | Authentication required. | Yes for protected areas. |
| `403` | Forbidden by Caddy or the app. | Yes for LAN-only sites hit from outside; investigate if a LAN user is blocked. |
| `404` | Not found. | A few are normal; a spike may mean broken links or scanners. |
| `500` | Server error. | Investigate. |
| `502` / `503` / `504` | Caddy could not successfully reach or use the upstream app. | Investigate the upstream app or network path. |

## Normal Things You May See

These can be normal:

- `403` on LAN-only services when tested from outside the LAN.
- `401` on protected status/admin paths.
- random requests for `/.env`, `/wp-login.php`, `/phpmyadmin`, and other scanner paths.
- some `404` entries from bots or old links.
- HTTP-to-HTTPS redirects on known hostnames.

## Things Worth Investigating

Look closer when you see:

- a public app returning lots of `500`, `502`, `503`, or `504`
- a sudden jump in `Failed Requests`
- a normally quiet service getting lots of traffic
- many `404`s for real app paths, not scanner paths
- a LAN user getting `403` on a LAN-only service
- Caddy reload failure mentioning `permission denied`
- `rp-logs.cfts.co` not updating for more than five minutes

## Minimal Linux Checks

Run commands one line at a time.

Check Caddy:

```bash
systemctl status caddy --no-pager -l
```

Validate Caddy config:

```bash
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
```

Reload Caddy after a valid config change:

```bash
sudo systemctl reload caddy
```

Read recent Caddy service errors:

```bash
journalctl -u caddy -n 80 --no-pager -l
```

Check the log dashboard generator:

```bash
systemctl status caddy-goaccess-report.service --no-pager -l
```

Check the dashboard refresh timer:

```bash
systemctl list-timers caddy-goaccess-report.timer --no-pager
```

Check the generated dashboard file:

```bash
ls -lh /var/www/caddy-log-report/index.html
```

## Common Problems And First Fixes

| Symptom | Likely cause | First place to look |
| --- | --- | --- |
| Caddy reload fails with `opening log writer` and `permission denied` | A configured log file does not exist or is not owned by `caddy`. | `Project-Documentation/01. Caddy Reverse Proxy Operations.md`, Access Log Permissions section. |
| `rp-logs.cfts.co` shows `403` | You are outside the LAN or not coming from `172.16.198.0/24`. | Test from LAN or VPN path that lands inside the allowed range. |
| Log dashboard stopped updating | Timer/service failed or GoAccess render failed. | `systemctl status caddy-goaccess-report.service --no-pager -l` |
| Dashboard installer says `Renderer source is stale` | The uploaded `observability/goaccess/render-caddy-goaccess-report.sh` is old. | Re-upload the full current `observability` folder. |
| Browser gets app error but Caddy looks healthy | Upstream app may be unhealthy. | Check the specific app VM/service named in the exposure matrix. |
| External user gets `403` for a LAN-only hostname | Expected behavior. | Confirm whether the hostname is listed as LAN-only in the exposure matrix. |

## How To Think About Changes

Before changing a hostname, access rule, upstream, or log file:

1. Check `Project-Documentation/02. rproxy Exposure Matrix.md`.
2. Make the smallest Caddy change possible.
3. Make sure any new log file exists and is owned by `caddy`.
4. Run `sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile`.
5. Reload Caddy.
6. Test the affected hostname in a browser or with `curl -I`.
7. Update the matrix and operations docs.

The safest mindset: Caddy is shared front-door infrastructure. Small changes, one at a time, with validation before reload.
