How to Secure Your Railway App
Railway makes deploying backend services, databases, and full-stack apps simple. But convenience can lead to security gaps if you do not configure access controls, manage secrets properly, and protect your database. This guide covers how to lock down your Railway deployment.
Find security issues automatically before attackers do.
Follow These Steps
Use Railway Variables for all secrets
Never hardcode credentials. Use Railway shared variables and service-specific variables to manage secrets across your project.
// Railway automatically injects variables at runtime
// Access them through process.env
const dbUrl = process.env.DATABASE_URL
const apiKey = process.env.STRIPE_SECRET_KEY
// Reference other services using Railway variable references
// In Railway dashboard: ${{Postgres.DATABASE_URL}}Configure private networking
Railway provides private networking between services. Use internal URLs for service-to-service communication instead of public URLs.
// Use Railway internal networking
// In your API service, connect to your database via internal URL
// DATABASE_URL = ${{Postgres.DATABASE_PRIVATE_URL}}
// For service-to-service calls, use internal DNS
// WORKER_URL = http://worker.railway.internal:3000
// Only expose services that need public accessServices without a public domain are only accessible via Railway private networking, which is secure by default.
Secure your Railway database
If using Railway Postgres, configure connection security and access controls.
// Use SSL for database connections
import pg from 'pg'
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production'
? { rejectUnauthorized: false }
: false
})
// Use parameterized queries
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[userId]
)Add security headers to your application
Configure security headers in your web framework. Railway does not add application security headers.
// Express.js
import helmet from 'helmet'
app.use(helmet())
// Or custom headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff')
res.setHeader('X-Frame-Options', 'DENY')
res.setHeader('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin')
next()
})Implement rate limiting
Railway services are publicly accessible. Add rate limiting to prevent abuse.
import rateLimit from 'express-rate-limit'
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many requests, try again later' }
})
app.use('/api/', apiLimiter)
// Stricter limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5
})
app.use('/api/auth/', authLimiter)Set up health checks and monitoring
Configure Railway health checks and add logging to detect security incidents.
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() })
})
// Log security-relevant events
app.use((req, res, next) => {
if (res.statusCode === 401 || res.statusCode === 403) {
console.warn(`Auth failure: ${req.method} ${req.path} from ${req.ip}`)
}
next()
})Scan your Railway deployment
After deploying, run a VAS scan against your public Railway URL to verify all security measures are in place.
Railway provides automatic HTTPS on custom domains and *.up.railway.app subdomains.
What You'll Achieve
Your Railway app now uses environment variables for secrets, private networking for internal communication, secured database connections, security headers, rate limiting, and monitoring. Your deployment follows security best practices.
Common Mistakes to Avoid
Mistake
Using public URLs for service-to-service communication
Fix
Use Railway private networking with internal DNS. Public URLs add unnecessary latency and expose internal services.
Mistake
Not setting up rate limiting on public endpoints
Fix
Add express-rate-limit or equivalent. Without it, attackers can abuse your API and exhaust Railway resources.
Mistake
Hardcoding database credentials in code
Fix
Use Railway variable references like ${{Postgres.DATABASE_URL}} to automatically inject database credentials.
Frequently Asked Questions
Does Railway provide HTTPS?
Yes. Railway provides automatic HTTPS on both *.up.railway.app subdomains and custom domains. No additional configuration is needed for transport security.
Are Railway services accessible from the internet?
Only if you assign a public domain. Services without a domain are only accessible via Railway private networking, which is secure by default.
How do I connect services securely on Railway?
Use Railway private networking. Services can communicate via internal DNS (service.railway.internal) without exposing traffic to the public internet.
Ready to Secure Your App?
VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.
Start Security Scan