3 months 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.
0 Replies
i will also note that in the sample sveltekit app for railway, csrf is disabled. not sure why
3 months 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?
3 months ago
we don't set x- forwarded-host but can't hurt to set the other two
3 months 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
3 months 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?
3 months 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-host
according to
did you say that at least the protocol header was being set by railway?
3 months ago
yes it is, see here -
when you set an env var is it injected into the environment at startup like
ORIGIN=[https://something](https://something) vite dev --host --port 8080
3 months ago
hold on, you are running a dev server on railway?
3 months 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?
3 months ago
you need to build and run the node-adapter
3 months 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;
3 months ago
yes, but you are likely still telling it to run dev
3 months ago
this starts a dev server, not the node-adapter
3 months 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
3 months ago
please run a production server as shown in my links
3 months 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
3 months 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
3 months 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!
3 months 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=production
on 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.ts
with 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"
3 months 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
3 months ago
hey as a support person, dev servers are the bane of my existence
3 months 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
3 months ago
!s
Status changed to Solved brody • 3 months ago