10 months ago
Hi!
I'm deploying a Vite SPA to Railway and need to serve the Apple App Site Association file at /.well-known/apple-app-site-association. This file:
- Has no file extension (Apple's requirement)
- Must be served with Content-Type: application/json
Using npx serve with the -s flag for SPA routing, the file returns index.html instead of the actual file contents. This is a https://github.com/vercel/serve-handler/issues/142 - it doesn't properly handle extensionless files in .well-known.
What I've tried:
1. serve.json with rewrites (renaming to .json and rewriting) - rewrites don't seem to apply
2. serve.json with cleanUrls: false - no effect
3. Running serve from inside the dist directory with -c flag - .well-known returns 404
Is there a recommended way to serve extensionless files with custom headers on Railway without a custom server? Perhaps a built-in nginx config option or another static file server that handles this well?
Pinned Solution
10 months ago
okay , you can create a lightweight Express server that properly handles the file
server.js :
import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const app = express();
const PORT = process.env.PORT || 3000;
const distPath = path.join(__dirname, 'dist');
app.get('/.well-known/apple-app-site-association', (req, res) => {
const filePath = path.join(distPath, '.well-known', 'apple-app-site-association');
if (fs.existsSync(filePath)) {
res.setHeader('Content-Type', 'application/json');
res.sendFile(filePath);
} else {
res.status(404).send('Not found');
}
});
app.use(express.static(distPath));
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
app.listen(PORT, () => {
console.logServer running on port ${PORT});
});
and package.json for Express setup :
{
"name": "your-vite-app",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"vite": "^5.0.0"
}
}
the railway setup for Express is a build command: npm install && npm run build and a Start Command: npm start no nixpacks.toml needed
5 Replies
10 months ago
Hey there! We've found the following might help you get unblocked faster:
- 🧵 Streamlit App Custom Icon Not Displaying on iOS Home Screen (apple-touch-icon Failure)
- 🧵 502 Connection Dial Timeout
- 🧵 Unable to get caddy reverse proxy working on railway
If you find the answer from one of these, please let us know by solving the thread!
10 months ago
add this Dockerfile at the root of your project :
# Multi-stage build
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source files
COPY . .
# Build the app
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy built app from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
10 months ago
Hi! Thanks, @domehane.
Is there a solution no involving setting up a Dockerfile? This app is inside a monorepo, and I'd rather avoid the additional complexity of directly maintaining a Dockerfile.
10 months ago
okay , you can create a lightweight Express server that properly handles the file
server.js :
import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const app = express();
const PORT = process.env.PORT || 3000;
const distPath = path.join(__dirname, 'dist');
app.get('/.well-known/apple-app-site-association', (req, res) => {
const filePath = path.join(distPath, '.well-known', 'apple-app-site-association');
if (fs.existsSync(filePath)) {
res.setHeader('Content-Type', 'application/json');
res.sendFile(filePath);
} else {
res.status(404).send('Not found');
}
});
app.use(express.static(distPath));
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
app.listen(PORT, () => {
console.logServer running on port ${PORT});
});
and package.json for Express setup :
{
"name": "your-vite-app",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"vite": "^5.0.0"
}
}
the railway setup for Express is a build command: npm install && npm run build and a Start Command: npm start no nixpacks.toml needed
10 months ago
Okay great, I ended up doing that! Thanks!
Status changed to Solved ray-chen • 10 months ago
