16 days ago
I have a Node.js/Express app with a Stripe checkout integration. The /create-checkout-session endpoint works perfectly when running locally (npm start on localhost:4242), but returns a 404 error when deployed to Railway.
Error Messages:
- Frontend:
Failed to load resource: the server responded with a status of 404 - Frontend:
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON - This indicates the server is returning an HTML error page instead of the expected JSON response
What I've tried:
- Pushed code to GitHub multiple times
- Redeployed on Railway
- Added environment variables (STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY)
- Code works perfectly locally
GitHub Repo:https://github.com/cookn1198/a-porchful-of-pumpkins
The /create-checkout-session endpoint is defined in Server.js (lines 16-38) but seems to not be accessible on the deployed Railway app, even though the server appears to start successfully.
2 Replies
Status changed to Open Railway • 16 days ago
16 days ago
Hello. I happened to come across your issue and wanted to see if Claude could make a suggestion. This is the response and I hope its helpful:
--------------------------------------------
This is a classic Railway deployment issue with two likely culprits — and they're both easy to fix.
🔴 Problem 1: Hardcoded localhost URL in the frontend (Most Likely Cause)
When you call /create-checkout-session locally, your frontend probably fetches something like:
fetch('http://localhost:4242/create-checkout-session', ...)
In production on Railway, localhost:4242 doesn't exist — the server is at your Railway public URL. So the request goes nowhere, and Railway's own 404 HTML page is returned instead of your JSON. That's exactly why you see:
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Fix: Use a relative URL so it always points to the same server serving the frontend:
// ❌ Broken in production
fetch('http://localhost:4242/create-checkout-session', ...)
// ✅ Works everywhere
fetch('/create-checkout-session', ...)
🔴 Problem 2: Railway isn't using the right port
Railway assigns a dynamic port via the PORT environment variable. If Server.js hardcodes port 4242, Railway's proxy can't reach the app.
Check your server startup code. It should look like this:
// ❌ Hardcoded — breaks on Railway
app.listen(4242, () => console.log('Running on 4242'));
// ✅ Correct — reads Railway's assigned port
const PORT = process.env.PORT || 4242;
app.listen(PORT, () => console.log(`Running on port ${PORT}`));
🟡 Problem 3: package.json start script mismatch
Railway looks at your package.json"start" script to know how to run the app. Make sure it matches your actual entry point:
"scripts": {
"start": "node Server.js"
}
If it says server.js (lowercase) but the file is Server.js (capital S), it will fail on Linux (Railway runs Linux, which is case-sensitive — unlike Windows/Mac).
Checklist to resolve
- Change
fetch('http://localhost:4242/...')→fetch('/create-checkout-session')in your frontend JS - Update server to use
process.env.PORTas shown above - Verify
package.json"start"script matches the exact filename casing ofServer.js - Redeploy — the fix should be immediate
The HTML-instead-of-JSON error is the dead giveaway that the fetch URL is pointing to the wrong place. Fix #1 alone will likely resolve it entirely.
16 days ago
@jedmond1971
Appreciate the help. I have been using Claude heavily in the development process and those issues don't seem to be the problem.