Fix localhost redirect URLs on Railway/reverse proxy deployments - #25
Merged
Merged
Conversation
Fixes localhost redirect bug on Railway and other reverse proxy deployments. When deployed behind a reverse proxy (e.g. Railway, nginx), routeCtx.request.url contains the internal routing URL (http://localhost:8080) instead of the public domain. This causes Stripe checkout redirects and billing portal return URLs to point to localhost instead of the production domain. Changes: - Use ctx.site.url (populated from SITE_URL env var) for all redirect URLs - Affects: checkout success/cancel URLs, billing portal return URLs - Strip trailing slashes from ctx.site.url for clean URL construction Requires SITE_URL environment variable to be set in production deployments. Co-authored-by: Timchosen Uzua <timchosen@gmail.com>
…irect bug Co-authored-by: Timchosen Uzua <timchosen@gmail.com>
Hardens against stale database options (emdash:site_url) that may contain localhost values from development. Creates a centralized helper that: - Validates ctx.site.url is configured - Strips trailing slashes for clean URL construction - Logs warnings if localhost is detected in production - Provides isProductionSiteUrl() utility for conditional logic This ensures Stripe redirect URLs, billing portal return URLs, and admin invite links always use the public domain even if database options are stale. Changes: - Add util/site-url.ts with getPublicSiteUrl() and isProductionSiteUrl() - Update checkout.ts, subscriptions-public.ts, customer-portal.ts to use helper - Helper throws if ctx.site.url is missing (config error) - Helper warns but allows localhost (useful for dev/test) Co-authored-by: Timchosen Uzua <timchosen@gmail.com>
Clarifies the two root causes: 1. Request URL extraction behind reverse proxy 2. Stale emdash:site_url database option Adds: - SQL queries to check and fix database options - Database option vs environment variable hierarchy explanation - getPublicSiteUrl() helper behavior documentation - Guidance on when to update database options Documents that parent is handling database cleanup for live demo. Co-authored-by: Timchosen Uzua <timchosen@gmail.com>
Quick reference guide for deploying DashCommerce on Railway, Heroku, GCP, AWS, etc. Covers: - Required environment variables (SITE_URL, DATABASE_URL, Stripe keys) - Post-deployment database verification - Common issues and fixes (localhost redirects, domain changes) - Platform-specific notes (Railway, Heroku, Cloudflare, Docker/K8s) - Verification commands and monitoring tips Co-authored-by: Timchosen Uzua <timchosen@gmail.com>
cavewebs
marked this pull request as ready for review
September 15, 2026 01:16
cavewebs
added a commit
that referenced
this pull request
Sep 15, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary Adds a patch changeset for PR #25 which fixed localhost redirect URLs on Railway/reverse proxy deployments. PR #25 was merged without a changeset, so npm will not publish the fix. This PR adds the required changeset to trigger a version bump and npm publish. ## Changes - ✅ Add patch changeset for `@dashcommerce/core` describing the Railway/reverse-proxy URL fix - ✅ Move `LOCALHOST_REDIRECT_FIX.md` and `OPERATOR_CHECKLIST.md` from repo root to `docs/` directory ## Changeset Details **Package:** `@dashcommerce/core` **Version bump:** patch **Summary:** Railway/reverse-proxy: use configured public site URL for Stripe success/cancel and portal return URLs instead of request.origin which can be localhost:PORT ## Context PR #25 implemented `getPublicSiteUrl()` helper to fix Stripe redirect URLs and admin invite links that were incorrectly pointing to `http://localhost:8080` instead of the public domain when deployed behind a reverse proxy (Railway, nginx, etc.). The fix ensures URLs are constructed using `ctx.site.url` (from `SITE_URL` env var) instead of `request.origin`. ## Related - Fixes the missing changeset from #25 - Organizes operational documentation in `docs/` directory <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-6c01642d-921e-5817-b6a6-6a71e7045e4e?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-6c01642d-921e-5817-b6a6-6a71e7045e4e&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On https://demo.dashcommerce.dev (Railway + Neon deployment), redirect URLs were using
http://localhost:8080instead of the public domain:http://localhost:8080/thank-you/<id>?session_id=...http://localhost:8080/vendor/...http://localhost:8080/accountRoot Causes (Two Issues)
1. Request URL Behind Reverse Proxy
The code was extracting the origin from
routeCtx.request.url, which contains the internal routing URL when behind a reverse proxy. Railway (and similar platforms) route traffic through an internal network where the app listens onlocalhost:8080, while the public domain is handled by the proxy.2. Stale Database Option
The live Neon database had:
While the environment variable was correct (
SITE_URL=https://demo.dashcommerce.dev), this stale database option may have been read by EmDash core or could interfere with site URL resolution.Solution
Code Hardening
Created
getPublicSiteUrl()helper inutil/site-url.tsthat:ctx.site.url(from SITE_URL env var) instead ofrequest.url.originisProductionSiteUrl()utility for conditional logicFiles changed:
packages/core/src/util/site-url.ts- New helper modulepackages/core/src/routes/checkout.ts- Stripe checkout success/cancel URLspackages/core/src/routes/subscriptions-public.ts- Billing portal return URLpackages/core/src/routes/customer-portal.ts- Customer portal return URLDatabase Cleanup (Recommended)
Parent is handling cleanup of the stale database option:
The code fix ensures correct behavior even if this option remains stale, but cleaning it up prevents debugging confusion.
Required Configuration
Deployments must set the
SITE_URLenvironment variable to the public domain:Railway already has this configured for the demo. No application code changes or restarts needed beyond deploying this PR.
Testing
To verify on the live demo after deployment:
https://demo.dashcommerce.dev/thank-you/...https://demo.dashcommerce.dev/vendor/...Documentation
See
LOCALHOST_REDIRECT_FIX.mdfor:Design Decision
The helper warns but allows localhost URLs to support development and testing environments. In production with
SITE_URLset correctly, no warnings will appear. This design:SITE_URLis completely missing