
Deploy Deploy Caddy for Single Page Apps
Host React, Vue or Svelte single page apps with Caddy
Just deployed
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, copydistinto the repowww/folder, and push. Deep links like/settings/profilefall back toindex.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.htmlline covers every client route. - Long cache headers on hashed assets: Vite emits content-hashed filenames, so
assets/can be cached for a long time. Keepindex.htmlonmax-age=0, must-revalidateso 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.UPSTREAMto itshost: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 needstry_files $uri /index.html;inside alocationblock. - Compression and TLS:
encode zstd gzipis 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 buildyourself and copydistintowww/. - 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_filesline 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 distis a quick local static server, not a production web server. - Cache headers: Caddy lets you set
Cache-Controlfor hashed assets andindex.htmlseparately. - Where serve wins: For a five-minute local preview with no config,
serveis 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.
- Fork the template repo so you can change
www/and the Caddyfile. - In your React, Vue or Svelte app, run
npm run buildto producedist. - Copy the contents of
distinto the repo'swww/folder. - Commit and push. Railway rebuilds the
caddyservice and serves the new files. - Open a deep link like
/settings/profileand refresh. A 200 with your app means thetry_filesfallback toindex.htmlworks. - Add cache headers for hashed assets in the Caddyfile. Cache hashed files for a year and make
index.htmlalways 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
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:
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
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.
Template Content