a year ago
Hello, deploying my Svelte4 + superforms app on railway I get '403 Cross-site POST is forbidden' when submitting a form. I've tracked this down to csrf handling in sveltekit. Specifically, even though my site is served on https, when the form submits, the request origin received is http.
Request URL: http://frontend-staging-1e0f.up.railway.app/manage/fleet/drivers/driver/397a54e7-f60a-49ba-8578-55176b334ab3
'sec-fetch-mode': 'cors',
Request origin: http://frontend-staging-1e0f.up.railway.app
'sec-fetch-site': 'same-origin',I don't understand how this happens. I've tried using x-forwarded-proto in my hooks.server.ts to try to adapt:
const forwardedProto = event.request.headers.get('x-forwarded-proto');
if (forwardedProto) {
// Update the URL protocol to match the original client request
event.url.protocol = `${forwardedProto}:`;
}and even verified that: X-Forwarded-Proto: https
but this hack doesn't make any difference.
Disabling CSRF in sveltekit solves the problem, but ofc I don't want to do that. I'm fully stumped. I'm 99% sure this is a railway issue, but not sure how to solve. Please help! Spent hours debugging this and no closer except to open a security hole here.
48 Replies
i will also note that in the sample sveltekit app for railway, csrf is disabled. not sure why
a year ago
we do indeed demux incoming public https requests down to http and set the X-Forwarded-Proto to https, so you just need to find a reliable way to tell sveletkit that the incoming request is indeed https
thank you for responding so quickly and on a sunday morning no less.
Claude is suggesting i can do this:
adapter: adapter({ protocol_header: 'X-Forwarded-Proto', host_header: 'X-Forwarded-Host', proxy_header: 'X-Forwarded-Proto', // Add this line }), csrf: { checkOrigin: true, },
do you have any other customers who's solved this?
a year ago
we don't set x- forwarded-host but can't hurt to set the other two
a year ago
not off the top of my head, this wouldn't be super related to Railway fwiw
i've been on railway for over a year and love it and tell all my colleagues to use it. launching my startup on it this week as well. but… this one's got me really stumped
a year ago
just gotta find a way to tell sveletkit that the incoming requests are made with https, the solution wouldn't be specific to railway in anyway though
on my last railway project i needed to implement caddy in front of my frontend. is that also needed for sveltekit then?
a year ago
no it's not, sveletkit has its own server
in theory, these env vars should work:
PROTOCOL_HEADER=x-forwarded-proto
HOST_HEADER=x-forwarded-hostaccording to
did you say that at least the protocol header was being set by railway?
a year ago
yes it is, see here -
when you set an env var is it injected into the environment at startup like
ORIGIN=https://something vite dev --host --port 8080
a year ago
hold on, you are running a dev server on railway?
a year ago
only run dev servers locally, never on cloud
so if i switch it to NODE_ENV=production, with ORIGIN set to the https domain, are you saying csrf should work?
a year ago
you need to build and run the node-adapter
a year ago
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter({
protocolheader: 'X-Forwarded-Proto', hostheader: 'X-Forwarded-Host',
}),
csrf: {
checkOrigin: true
},
alias: {
'@regulis/common': '../common/dist',
$lib: './src/lib',
$serverLib: './src/lib'
}
}
};
export default config;
a year ago
yes, but you are likely still telling it to run dev
a year ago
this starts a dev server, not the node-adapter
a year ago
dont know, but best to start by running your app correctly
i'll try with ORIGIN set to the https url and with NODE_ENV=production and see if there's any diff.
part of the challenge here is that debugging a production app is quite difficult since logs are suppressed
a year ago
please run a production server as shown in my links
a year ago
it doesn't matter what you set NODE_ENV to if you are still running vite dev
i'm doing it. the only diff i have in my package script is for startPORT=8080 node build
instead of node build/index.js not sure if index.js is strictly required or not
a year ago
what command is being ran as the start command for your railway services
i have a monorepo so i define RAILWAY_SERVICE depending on which service i'm spinning up
a year ago
so you aren't running a dev server on railway?
well, the package.json actually has a fork on NODE_ENV"start": "if [ \"$NODE_ENV\" = \"development\" ]; then pnpm dev; else PORT=8080 node build; fi"
i know, kind of janky. i was trying to make it possible to run dev on staging in railway to maek things easier to debug. maybe shot myself in the foot here!
a year ago
just have it run the index file, no need to complicate
HALLELUJAH! It is working now! YOu were right about production mode. damn.
for anybody coming to this thread in the future, here are my takeaways for sveltekit on Railway
Always use
NODE_ENV=productionon railwaymake sure to set your ORIGIN header to your frontend's domain as either set by you or railway
turn on csrf in your
svelte.config.tswith these additional adapter headers:
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter({
protocol_header: 'X-Forwarded-Proto',
host_header: 'X-Forwarded-Host',
}),
csrf: {
checkOrigin: true
},Possibly, include these additional headers (maybe not)
```
PROTOCOLHEADER="x-forwarded-proto" HOSTHEADER="x-forwarded-host"
PORT_HEADER="x-forwarded-port"
a year ago
awesome!
thank you SO much for all your help today. if i had any hair left on my skull it'd be gone by now
a year ago
hey as a support person, dev servers are the bane of my existence
a year ago
vercel might monkey patch your project to make sure it's always running a production server, but railway only runs what you tell it to
a year ago
!s
Status changed to Solved brody • about 1 year ago