Railway
Insecure Headers

Insecure Headers on Railway

Railway provides HTTPS and automatic TLS certificate management, but application-level security headers must be set by the application itself. Most Railway deployments serve responses without Content-Security-Policy, X-Frame-Options, or Strict-Transport-Security.

Scan Your Railway App

How It Happens

Railway deploys Docker containers or buildpack-detected applications behind a reverse proxy that handles TLS termination. The proxy provides HTTPS but does not inject security headers into application responses. This means every header must be set by the application code. Railway's streamlined deployment experience encourages speed. Developers push code, Railway detects the framework, builds the container, and deploys it. At no point in this pipeline are security headers configured. The default Express, Flask, FastAPI, or Next.js server does not set security headers either. Railway does not have a platform-level headers configuration (like Vercel's vercel.json or Netlify's _headers file). The only way to add headers is in the application code itself, which means developers must modify their server configuration rather than a deployment config file.

Impact

Without Content-Security-Policy, XSS vulnerabilities in Railway-hosted applications are fully exploitable. CSP is the primary defense-in-depth mechanism against script injection, and its absence means any XSS vector can load external scripts, exfiltrate data, and modify page content without restriction. Missing X-Frame-Options on Railway-hosted applications allows embedding in iframes for clickjacking attacks. This is especially dangerous for SaaS applications, dashboards, and admin panels frequently deployed to Railway. Without HSTS, users who access the Railway app via HTTP (before the redirect to HTTPS) are vulnerable to man-in-the-middle attacks. Railway redirects HTTP to HTTPS, but without HSTS the browser does not remember to use HTTPS on subsequent visits.

How to Detect

Run curl -I https://your-app.up.railway.app to check the response headers from your Railway deployment. Verify the presence of Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security, Referrer-Policy, and Permissions-Policy. If your Railway service runs multiple routes, check headers on several paths. API routes and page routes may have different header configurations depending on how the application is structured. Vibe App Scanner checks all security-relevant response headers on Railway-hosted applications and provides framework-specific remediation guidance for the detected runtime.

How to Fix

For Express/Node.js on Railway, use the helmet middleware. Install it with npm install helmet and add app.use(helmet()) before your routes. Helmet configures sensible defaults for all major security headers. For Python FastAPI, add a middleware that sets security headers on every response. Use app.add_middleware(SecurityHeadersMiddleware) with a custom middleware class that sets all required headers. For Next.js on Railway, configure headers in next.config.js using the async headers() function, identical to the Vercel configuration. This approach is framework-native and works regardless of the hosting platform. For Go applications, add security headers in a middleware wrapper that applies to all HTTP handlers. Set the headers on the response writer before calling the next handler in the chain.

Code Examples

Security headers for a FastAPI app on Railway

Vulnerable
# Railway FastAPI app - no security headers
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}
Secure
from fastapi import FastAPI, Request, Response
from starlette.middleware.base import (
    BaseHTTPMiddleware,
    RequestResponseEndpoint,
)

class SecurityHeaders(BaseHTTPMiddleware):
    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        response = await call_next(request)
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["Strict-Transport-Security"] = (
            "max-age=63072000; includeSubDomains; preload"
        )
        response.headers["Referrer-Policy"] = (
            "strict-origin-when-cross-origin"
        )
        return response

app = FastAPI()
app.add_middleware(SecurityHeaders)

Frequently Asked Questions

Does Railway add any security headers automatically?

Railway handles HTTPS and TLS termination at the proxy level, but does not add application-level security headers. Your application code must set headers like CSP, X-Frame-Options, and HSTS explicitly.

Can I configure headers at the Railway platform level?

Unlike Vercel (vercel.json) or Netlify (_headers file), Railway does not have a platform-level header configuration. Headers must be set in your application code using framework middleware or response configuration.

Does the Railway proxy add X-Forwarded headers?

Yes. Railway's reverse proxy adds X-Forwarded-For, X-Forwarded-Proto, and similar headers. Your application can use X-Forwarded-Proto to detect HTTPS and set HSTS accordingly. But standard security headers like CSP and X-Frame-Options must be added by your application.

Is Your App Vulnerable?

VAS automatically scans for insecure headers and other security issues in Railway apps. Get actionable results with step-by-step fixes.

Scans from $5, results in minutes.