404 despite successful deployment and propogation of DNS
scrutinise
HOBBYOP

4 months ago

The logs show the server is running correctly:

  • Container started: "Starting Container" → No build/deploy errors.

  • Server running: "server running" — Next.js app is listening on port 8080.

  • Requests handled: "handled request" every few minutes — traffic is reaching your app.

  • Handler errors: "error handling handler error" — this is a non-fatal warning from Caddy (Railway's reverse proxy). It often means a 404 for unmatched routes, but since my app is Next.js, it should catch all paths (e.g., / → homepage). This is common in logs and doesn't crash the app.

The "Not Found at the station" page (404) when accessing www.scrutinise.org means traffic reaches Railway, but the proxy isn't routing to my app. This is not a DNS issue (propagation is complete — green on dnschecker.org). It's a proxy/routing delay with the Metal Edge (beta) builder or port mismatch. The "Not Secure" warning is because the HTTPS certificate isn't fully active yet (it auto-provisions after routing works).

My app is running — it's just not connected to the domain yet.

How do I fix this?

Solved$10 Bounty

Pinned Solution

4 months ago

you need to Install cross-env and make the start scripts portable and bind to Railway’s env port: npm i -D cross-env, set "start": "next start -p $PORT -H 0.0.0.0" and (for local) "start:local": "cross-env PORT=3000 next start -p $PORT -H 0.0.0.0", removing any set PORT=... hardcoding.

In Railway, set PORT=8080, ensure the service listens/exposes port 8080 in Networking, detach/re-attach the custom domain, then redeploy once to refresh routing and re-issue TLS. For your package.json, you can use the below:

{
  "name": "scrutinise-web",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": ">=20"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start -p $PORT -H 0.0.0.0",
    "start:local": "cross-env PORT=3000 next start -p $PORT -H 0.0.0.0",
    "lint": "next lint"
  },
  "dependencies": {
    "@clerk/nextjs": "^6.34.1",
    "@tailwindcss/postcss": "^4.1.16",
    "next": "16.0.1",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^24.10.0",
    "@types/react": "^19.2.2",
    "autoprefixer": "^10.4.21",
    "cross-env": "^7.0.3",
    "postcss": "^8.5.6",
    "tailwindcss": "^4.1.16",
    "typescript": "^5.9.3"
  }
}

You can then boot it up manually via npm run start:localTechnically, you can fix this as well to only run npm run start, but you'll need to modify your other scripts, so something like

const { spawn } = require("child_process");
const port = process.env.PORT || "3000"; // Railway sets 8080; local falls back to 3000

const nextBin = require.resolve("next/dist/bin/next");
const args = ["start", "-p", port, "-H", "0.0.0.0"];

const child = spawn(process.execPath, [nextBin, ...args], {
  stdio: "inherit",
  env: { ...process.env, PORT: port }
});

child.on("exit", (code) => process.exit(code ?? 0));

and your package.json would be:

{
  "name": "scrutinise-web",
  "version": "0.1.0",
  "private": true,
  "engines": { "node": ">=20" },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "node scripts/start.js",
    "lint": "next lint"
  },
  "dependencies": {
    "@clerk/nextjs": "^6.34.1",
    "@tailwindcss/postcss": "^4.1.16",
    "next": "16.0.1",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^24.10.0",
    "@types/react": "^19.2.2",
    "autoprefixer": "^10.4.21",
    "postcss": "^8.5.6",
    "tailwindcss": "^4.1.16",
    "typescript": "^5.9.3"
  }
}

hope this helped! lemme know if you run into any other issues

Attachments

6 Replies

Railway
BOT

4 months ago

Hey there! We've found the following might help you get unblocked faster:

If you find the answer from one of these, please let us know by solving the thread!


4 months ago

hey, visiting your site, I get an SSL error.

Websites prove their identity via certificates. Nightly does not trust this site because it uses a certificate that is not valid for www.scrutinise.org. The certificate is only valid for *.up.railway.app. Error code: SSL_ERROR_BAD_CERT_DOMAIN

what are your log requests to /? did you set the start command to be next start -p $PORT -H 0.0.0.0

you can attempt to detach your domain, and re-attach it, and then redeploy the container. are you using railway to issue SSL or CF?


scrutinise
HOBBYOP

4 months ago

Thanks for the quick reply and suggestions — I appreciate the help!

  • Logs for requests to /: Here's a snippet from my Deploy Logs (via GitHub push, 1 hour ago). The app starts fine, but requests show "handled request" with "error handling handler error" (Caddy proxy warnings), and no app-specific logs like "GET / 200". No 300/404 from Next.js — it seems like the proxy isn't forwarding to the container.

  • Start command: Yes, I updated to "start": "next start -p $PORT -H 0.0.0.0" in package.json and redeployed. Logs show "server running", but no "Listening on 0.0.0.0:8080" confirmation. PORT=8080 is in Variables.

  • Detach/reattach domain: Done — removed from Railway Networking, cleared FastHosts DNS, re-added CNAME @ and www to sn95qv.up.railway.app, redeployed. Still 404 "Not Found at the station" with Request ID BlwopYOQQKu-mcOgBT7zVQ (from error page). Propagation is 100% green on dnschecker.org.

  • SSL issuance: Using Railway's auto-issuance (Let's Encrypt) — no Cloudflare. Browser shows "SSL_ERROR_BAD_CERT_DOMAIN" (cert only valid for *.up.railway.app, not www.scrutinise.org). No manual provision button, but "Metal Edge - Setup Complete". App works on generated URL (miraculous-nature.up.railway.app), so it's a proxy/cert mismatch.

This matches issues in Production Routing Error: Flask App Running, but API Routes 404 and Requests to backend URL return 404 with no logs — proxy not forwarding despite app healthy.

Any ideas on forcing proxy refresh or cert re-issue? Happy to share more logs/project details.

Thanks!


4 months ago

check https://www.sslshopper.com/ssl-checker.html#hostname=www.scrutinise.org - there seems to be an issue with the cert, but it seems fine on the generated URL. you can:

  1. delete the generated URL by railway and re-expose the 8080 port

  2. re-attach your custom domain and explicitly put the 8080 port.

having both the app URL + your own domain won't work if both are trying to hit 8080 port. even checking your CNAME, it looks to be fine. one thing you can also do is entirely remove the CNAME DNS towards Railway, delete the custom domain, and re-attach them as new (since it'll issue a new *.up.railway.app URL) that you can rehook towards your CNAME.

after doing that, id redeploy your container to make sure it worked


scrutinise
HOBBYOP

4 months ago

Thank you all for your help, which has moved me on.

Sadly I'm still stuck on what appears to be port issues. Repeated 404 page not found.

Maybe the following additional background will show up an obvious mistake to someone?

I attach a spreadsheet listing the most recent iterations of my attempts to solve my port mismatch. Most have revolved around the start line in the package.json file. Some of my changes break deployment, some break the localhost version. None have yet connected me to port 8080 where Railway seems to be hiding out. I have a variable "PORT" set to "8080".

The localhost is on port 3000, so getting the localhost to work on 3000 and then Railway to work on 8080 may be part of the confusion. My local machine is also Windows, which has caused issues with code working locally but not on railway. My next step is to set up Linux (Ubuntu 22.04 — same as Railway) so I can run my local on the same system and remove one complexity and move the local to port 8080 so no dual port confusion and local/railway are as identical as possible.

Localhost breakage: Started with standalone mode (step 6) — lost PORT binding. Fixed repeatedly with hardcoded set PORT=3000, but it breaks when we try to make it cross-platform to work on local windows machine and Railway linux (Next step put a Linux installation on my laptop so I'm not trying to make it work cross platform).

  • Deployment success: All builds green after lockfile, but healthcheck fails since step 6 (standalone mode + no explicit PORT in start script).

  • What was achieved: Stable build, localhost working, domain attached — but port mismatch is the blocker.

  • What broke it: Standalone mode is good for production, but the start script needs explicit PORT from env (not hardcoded). The variable PORT=8080 is set, but the script isn't using it.

The Healthcare check is a current but linked problem:

Likely Cause: The server starts but binds to the wrong port (3000 instead of 8080). Railway's proxy sends healthcheck requests to 8080, but the server is listening on 3000, so the request never reaches the app → "service unavailable". This started after standalone mode (step 6) because server.js defaults to 3000 if PORT is not passed correctly.

Why it wasn't there before: Pre-standalone, next start used the default binding, but Railway's proxy could sometimes "guess" it. Standalone is stricter — it requires explicit PORT from env.

Current Plan:Explicit PORT binding in start script using env var (works for localhost + Railway). No more hardcoded numbers.

Cause of 404: Same as healthcheck — port mismatch. The proxy sends traffic to 8080, but server on 3000 → nothing there → 404. Root domain is "setup complete" but not responding because of this.

Anyway I have a couple of items to be getting on with but if there's anything obvious I'm missing that I ought to try all ideas welcome!

Thank you

Charlie


scrutinise

Thank you all for your help, which has moved me on.Sadly I'm still stuck on what appears to be port issues. Repeated 404 page not found.Maybe the following additional background will show up an obvious mistake to someone?I attach a spreadsheet listing the most recent iterations of my attempts to solve my port mismatch. Most have revolved around the start line in the package.json file. Some of my changes break deployment, some break the localhost version. None have yet connected me to port 8080 where Railway seems to be hiding out. I have a variable "PORT" set to "8080".The localhost is on port 3000, so getting the localhost to work on 3000 and then Railway to work on 8080 may be part of the confusion. My local machine is also Windows, which has caused issues with code working locally but not on railway. My next step is to set up Linux (Ubuntu 22.04 — same as Railway) so I can run my local on the same system and remove one complexity and move the local to port 8080 so no dual port confusion and local/railway are as identical as possible.Localhost breakage: Started with standalone mode (step 6) — lost PORT binding. Fixed repeatedly with hardcoded set PORT=3000, but it breaks when we try to make it cross-platform to work on local windows machine and Railway linux (Next step put a Linux installation on my laptop so I'm not trying to make it work cross platform).Deployment success: All builds green after lockfile, but healthcheck fails since step 6 (standalone mode + no explicit PORT in start script).What was achieved: Stable build, localhost working, domain attached — but port mismatch is the blocker.What broke it: Standalone mode is good for production, but the start script needs explicit PORT from env (not hardcoded). The variable PORT=8080 is set, but the script isn't using it.The Healthcare check is a current but linked problem:Likely Cause: The server starts but binds to the wrong port (3000 instead of 8080). Railway's proxy sends healthcheck requests to 8080, but the server is listening on 3000, so the request never reaches the app → "service unavailable". This started after standalone mode (step 6) because server.js defaults to 3000 if PORT is not passed correctly.Why it wasn't there before: Pre-standalone, next start used the default binding, but Railway's proxy could sometimes "guess" it. Standalone is stricter — it requires explicit PORT from env.Current Plan:Explicit PORT binding in start script using env var (works for localhost + Railway). No more hardcoded numbers.Cause of 404: Same as healthcheck — port mismatch. The proxy sends traffic to 8080, but server on 3000 → nothing there → 404. Root domain is "setup complete" but not responding because of this.Anyway I have a couple of items to be getting on with but if there's anything obvious I'm missing that I ought to try all ideas welcome!Thank youCharlie

4 months ago

you need to Install cross-env and make the start scripts portable and bind to Railway’s env port: npm i -D cross-env, set "start": "next start -p $PORT -H 0.0.0.0" and (for local) "start:local": "cross-env PORT=3000 next start -p $PORT -H 0.0.0.0", removing any set PORT=... hardcoding.

In Railway, set PORT=8080, ensure the service listens/exposes port 8080 in Networking, detach/re-attach the custom domain, then redeploy once to refresh routing and re-issue TLS. For your package.json, you can use the below:

{
  "name": "scrutinise-web",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": ">=20"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start -p $PORT -H 0.0.0.0",
    "start:local": "cross-env PORT=3000 next start -p $PORT -H 0.0.0.0",
    "lint": "next lint"
  },
  "dependencies": {
    "@clerk/nextjs": "^6.34.1",
    "@tailwindcss/postcss": "^4.1.16",
    "next": "16.0.1",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^24.10.0",
    "@types/react": "^19.2.2",
    "autoprefixer": "^10.4.21",
    "cross-env": "^7.0.3",
    "postcss": "^8.5.6",
    "tailwindcss": "^4.1.16",
    "typescript": "^5.9.3"
  }
}

You can then boot it up manually via npm run start:localTechnically, you can fix this as well to only run npm run start, but you'll need to modify your other scripts, so something like

const { spawn } = require("child_process");
const port = process.env.PORT || "3000"; // Railway sets 8080; local falls back to 3000

const nextBin = require.resolve("next/dist/bin/next");
const args = ["start", "-p", port, "-H", "0.0.0.0"];

const child = spawn(process.execPath, [nextBin, ...args], {
  stdio: "inherit",
  env: { ...process.env, PORT: port }
});

child.on("exit", (code) => process.exit(code ?? 0));

and your package.json would be:

{
  "name": "scrutinise-web",
  "version": "0.1.0",
  "private": true,
  "engines": { "node": ">=20" },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "node scripts/start.js",
    "lint": "next lint"
  },
  "dependencies": {
    "@clerk/nextjs": "^6.34.1",
    "@tailwindcss/postcss": "^4.1.16",
    "next": "16.0.1",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^24.10.0",
    "@types/react": "^19.2.2",
    "autoprefixer": "^10.4.21",
    "postcss": "^8.5.6",
    "tailwindcss": "^4.1.16",
    "typescript": "^5.9.3"
  }
}

hope this helped! lemme know if you run into any other issues

Attachments


Status changed to Solved ray-chen 4 months ago


Loading...