---
title: "Deploy Deploy Caddy for Single Page Apps"
description: "Host React, Vue or Svelte single page apps with Caddy"
category: "Starters"
url: https://railway.com/deploy/deploy-caddy-for-single-page-apps
---

# Deploy Deploy Caddy for Single Page Apps

Host React, Vue or Svelte single page apps with Caddy

**[Deploy Deploy Caddy for Single Page Apps on Railway](https://railway.com/template/deploy-caddy-for-single-page-apps)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/deploy-caddy-for-single-page-apps/manifest.json

- **Creator:** onepush
- **Category:** Starters

## Template content

### caddy https://github.com/caddyserver.png

- **Image:** ghcr.io/onepush-dev/caddy:2.11.7
- **Health check:** /
- **Public domain:** Yes

## Documentation

# Deploy and Host self hosted Caddy SPA Hosting (Static Hosting for Single Page Apps) on Railway

This template serves your built React, Vue or Svelte app from /srv with try_files falling back to index.html, so a deep link like /settings/profile never 404s on refresh.

## About Hosting Caddy SPA Hosting open-source software on Railway (self hosted Caddy for single page apps template)

This template runs one Caddy service named `caddy` that serves your built React, Vue or Svelte app from `/srv`, filled from the repo `www/` folder.

## Why Deploy Caddy SPA Hosting, the Nginx alternative on Railway (Railway Free Trial)

Nginx can serve a React or Vue build, but you have to write the `try_files` fallback to `/index.html` yourself. Copy your `dist` output into `www/` and Railway serves it from `/srv`.

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Caddy SPA Hosting on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

### Railway vs Other Hosting Providers and VPS for Caddy SPA Hosting self hosting

This table compares the Railway Caddy SPA template against common raw VPS and cloud options, focused on what you set up yourself for deep links to work.

| Provider | What You Get with Railway | What You Get with the Other Provider |
|---|---|---|
| DigitalOcean | A Caddy service with the SPA fallback already set, redeployed when you push. | A Droplet where you install Caddy, write the try_files fallback, and patch the OS. |
| AWS | The same Caddy service with no IAM or bucket rules to set before deep links work. | S3 or EC2 setups that need error document rules or a CloudFront function for index.html fallback. |
| Hetzner | Git-driven redeploys with no SSH or deploy script on your side. | Cheap servers with generous bandwidth, but Caddy, systemd and builds are all manual. |

A VPS gives you more knobs, while the template gets a React build deep-linking correctly with less upkeep.

## Common Use Cases for hosted Caddy SPA Hosting

- **Vite React build with React Router:** Run `npm run build`, copy `dist` into the repo `www/` folder, and push. Deep links like `/settings/profile` fall back to `index.html`, so React Router handles them instead of Caddy returning a 404 on refresh.
- **Vue or Svelte history-mode routing:** Any SPA using the HTML5 history API gets the same fallback. One `try_files {path} /index.html` line covers every client route.
- **Long cache headers on hashed assets:** Vite emits content-hashed filenames, so `assets/` can be cached for a long time. Keep `index.html` on `max-age=0, must-revalidate` so new deploys show up right away.
- **A staging copy of a frontend:** Deploy a second copy of the template with a different build to check a branch first. It runs as its own Railway service, so production files stay untouched.
- **API behind the same domain later:** When your frontend needs a backend on the same origin, set `caddy.UPSTREAM` to its `host:port`. Caddy switches to reverse-proxy mode without Caddyfile edits, which sidesteps CORS setup.

## Dependencies for Caddy SPA Hosting Docker hosted on Railway

This template runs as one Caddy service, so there is little to wire up. No database, no Redis, no second container.

### Deployment Dependencies for Managed Caddy SPA Hosting Service (Web Server)

Run your frontend build, then copy the dist folder into www so it lands at /srv in the container. The service has no deploy-time inputs and no secrets.

### Implementation Details for Caddy SPA Hosting (Using Caddy official docker image)

The image is ghcr.io/onepush-dev/caddy:2.11.7, wrapping the official caddy:2.11.7-alpine build with no extra plugins.

## How does Caddy SPA Hosting compare against other ways to host a single page app

### Caddy SPA Hosting vs Nginx (Nginx Alternative)

- **Config size:** The SPA fallback is one line, `try_files {path} /index.html`. Nginx needs `try_files $uri /index.html;` inside a `location` block.
- **Compression and TLS:** `encode zstd gzip` is already set here. Nginx wins if you need a mature module ecosystem or config you've already tuned.
- **Ops familiarity:** Plenty of teams already know Nginx's failure modes. Caddy has a short learning curve.

### Caddy SPA Hosting vs Netlify (Netlify Alternative)

- **Control:** You own the container, the Caddyfile and the cache headers. Netlify includes a CDN and build pipeline that this template doesn't.
- **Builds:** Netlify builds on every push. Here you run `npm run build` yourself and copy `dist` into `www/`.
- **Where Netlify wins:** Previews, edge functions and global CDN delivery would need to be built yourself with Caddy.

### Caddy SPA Hosting vs Vercel (Vercel Alternative)

- **Deploy flow:** Vercel creates preview URLs for every branch. This template deploys whatever you last copied into `www/`.
- **Framework handling:** Vercel configures routing for Next.js, Vite and others. Here the `try_files` line is already set for you.
- **Where Vercel wins:** Zero-config previews and framework-aware builds save real time for teams.

### Caddy SPA Hosting vs serve (npm) (serve Alternative)

- **Purpose:** `npx serve dist` is a quick local static server, not a production web server.
- **Cache headers:** Caddy lets you set `Cache-Control` for hashed assets and `index.html` separately.
- **Where serve wins:** For a five-minute local preview with no config, `serve` is faster.

## How to use Caddy SPA Hosting (the OSS web server and reverse proxy)?

Day one is a build, copy, push loop. Caddy serves the built SPA from `/srv`, which comes from the repo `www/` folder.

1. Fork the template repo so you can change `www/` and the Caddyfile.
2. In your React, Vue or Svelte app, run `npm run build` to produce `dist`.
3. Copy the contents of `dist` into the repo's `www/` folder.
4. Commit and push. Railway rebuilds the `caddy` service and serves the new files.
5. Open a deep link like `/settings/profile` and refresh. A 200 with your app means the `try_files` fallback to `index.html` works.
6. Add cache headers for hashed assets in the Caddyfile. Cache hashed files for a year and make `index.html` always revalidate.

```
try_files {path} /index.html

route {
    header /assets/* Cache-Control "public, max-age=31536000, immutable"
    header /index.html Cache-Control "public, max-age=0, must-revalidate"
}
```

## How to self host Caddy SPA Hosting on other VPS Services (Caddy self hosting guide)

You can run this same Caddy setup on any VPS with Docker. The steps below assume a Linux box with Docker and Git installed, and that your domain points at the server.

### Clone the Repository

```bash
git clone https://github.com/your-org/caddy-spa-hosting.git
cd caddy-spa-hosting
```

Build your React, Vue or Svelte app first and copy its `dist` output into the repo `www/` folder. Caddy serves that folder from `/srv`.

### Install Dependencies

The stack needs only Docker and Docker Compose, so there is no Node runtime on the server.

### Configure Environment Variables

Create a `.env` file next to the compose file:

```bash
PORT=8080
UPSTREAM=
```

Leave `UPSTREAM` empty for static mode. Set it to `your-api-host:3000` only if you want Caddy to reverse-proxy an API on the same domain.

### Start the Caddy SPA Hosting Application

```bash
docker compose up -d --build
curl -I http://localhost:8080/settings/profile
```

The deep-link request should return `200` and the app's `index.html`, which is the `try_files` fallback doing its job.

## Official Pricing of Caddy SPA Hosting (Caddy pricing)

Caddy is free and open source under the Apache-2.0 license, so there is no license fee. You pay only for the infrastructure it runs on. Check Railway's pricing page for current rates.

## Caddy SPA Hosting cloud vs self hosted comparison (Pricing, features, costs, and more)

Running Caddy SPA Hosting on Railway means paying for one small Caddy service and nothing else. Here's how the two paths compare.

### Monthly cost of self hosting Caddy SPA Hosting on Railway

A single Caddy service serving a static React, Vue or Svelte build is light on resources. Confirm current rates on Railway's pricing page, since they change.

### System Requirements for Hosting Caddy SPA Hosting on a VPS

Caddy is a single static binary, so a small VPS with a few hundred megabytes of RAM is plenty for a static site. You need Docker or the Caddy binary installed and a public IP for DNS.

## Frequently Asked Questions (FAQs)

### Why do deep links 404 on some hosts?

Most static hosts look for a file at the exact path, so `/settings/profile` returns 404 on refresh.

### Do I need to set cache headers myself?

Hashed assets under `/assets` can be cached for a long time, while `index.html` should revalidate on every load so users get new builds. Add those `header` rules in a `route` block in the Caddyfile.

### Are environment variables read at build time or runtime?

For Vite-style SPAs, `VITE_` variables are baked into the bundle during `npm run build`. Runtime variables like `UPSTREAM` only control the server.

### How do I attach a custom domain?

Add the domain in Railway's service networking settings and point your DNS at the target Railway gives you. TLS terminates at Railway's edge, so the container runs with `auto_https off`.

### Does Caddy compress files with gzip and zstd?

Yes, the template uses `encode gzip zstd`, so responses are compressed on the fly for clients that accept either format. Pre-built `.br` or `.zst` sidecar files need the separate `precompressed` option.

### When should I set UPSTREAM?

Set `caddy.UPSTREAM` to `host:port` when the same domain should proxy API calls to another service, such as a backend on Railway's private network.


## Similar templates

- [Openfront Gym](https://railway.com/deploy/openfront-gym) — Opensource Mindbody alternative based on Next.js and Keystone.js
- [open-excalidraw](https://railway.com/deploy/open-excalidraw) — Self-hostable collaborative drawing built on Excalidraw
- [Next.js + Prisma + PostgreSQL Starter](https://railway.com/deploy/nextjs-prisma-postgres) — Deploy a Next.js app with Prisma ORM and PostgreSQL in one click.

Open this page in a browser: https://railway.com/deploy/deploy-caddy-for-single-page-apps
