a day ago
Title: Edge proxy returns 502 for paths with invalid percent-encoding
Project: witgoed-aanbod (service "witgoed-aanbod", custom domain www.witgoedaanbod.nl, region EU / edge ams1)
Summary:
Any request whose PATH contains an invalid percent-sequence (a raw "%" not followed by two hex digits) is answered by the Railway edge proxy with "502 Bad Gateway / upstream error" and never reaches our deployed service. The same sequence in the QUERY STRING is forwarded fine.
Reproduction (works on any path, the product does not need to exist):
curl -i "https://www.witgoedaanbod.nl/%-"
-> HTTP/1.1 502 Bad Gateway
content-type: text/plain; charset=utf-8
x-hikari-trace: ams1.b55h
server: railway-hikari
body: "upstream error"Control cases:
/product/proef-geldig-%41-123 -> 404 (valid encoding, reaches the app)
/?q=%zz -> 200 (invalid encoding in query is fine)
/product/proef-ongeldig-%-123 -> 502 (invalid encoding in path)
Evidence that the request never reaches our service:
On 2026-08-19 around 12:34-12:37 CEST we fired 10 requests with an invalid
percent path and 2 control requests to /product/proef-normaal-404. The
service's HTTP logs (Network Logs > HTTP) show both control requests
(404, ~10 ms) but none of the invalid-percent requests. Our app itself
(Flask behind gunicorn 21.2.0) answers a clean 404 for these exact paths
when tested directly with a local server, so the 502 originates in the
edge proxy, not in our application.
Impact:
Googlebot is repeatedly recrawling 9 legacy product URLs that contain a
raw "%" (old slugs, since fixed on our side). Because the edge answers
502 instead of forwarding, we cannot serve the intended 404/301, Google
Search Console keeps reporting server errors (5xx) for our domain, and a
failed validation is hurting how Google crawls the rest of the site. We
have no way to fix this from inside the app: the request never arrives.
Expected behaviour:
Either forward the path as-is (both gunicorn and Werkzeug handle invalid
percent-sequences gracefully), or answer with a 4xx client error. A 502
suggests our service is down, which it is not, and a 5xx status makes
crawlers retry indefinitely.
Question:
Can the edge proxy's handling of invalid percent-encoding in paths be
fixed or configured? If not, is there a workaround you recommend so these
URLs return a 4xx instead of 502?
1 Replies
a day ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • 1 day ago
44 minutes ago
Two things: your diagnosis is right and only Railway can change the edge behaviour, but the SEO damage you are worried about is smaller than you think, and there is a workaround you can deploy today.
On the impact
Google's documentation on how status codes affect crawling states that the crawl-rate reduction from server errors is "proportionate to the number of individual URLs that are returning a server error". Nine legacy URLs against your whole catalogue is a rounding error — it will not meaningfully change how the rest of the site is crawled, and the failed validation in Search Console is reporting those URLs specifically rather than applying a site-level penalty. Worth fixing, but not worth a risky infrastructure change under time pressure.
The part that is genuinely bad: on 5xx, Google preserves the indexed URL and keeps retrying rather than dropping it. A 404 or 410 would let it drop them and close the report. That is the real cost of the 502 — left alone, these never resolve on their own.
Why you cannot fix this from inside the app
I checked your DNS: www.witgoedaanbod.nl is a CNAME straight to chx5lsu5.up.railway.app (69.46.46.54). Railway's edge is the first thing that touches the request and there is nothing in front of it today, so no application-level change can help. You are right about that.
The workaround: put something in front of Railway
Move the domain's nameservers to Cloudflare (the free plan is enough), enable the proxy on www, and answer these URLs at Cloudflare before they ever reach Railway's edge. A Worker is the reliable mechanism here, because Cloudflare's rule engine normalizes http.request.uri.path and you specifically need the raw path:
export default { async fetch(request) { const p = new URL(request.url).pathname; if (/%(?![0-9A-Fa-f]{2})/.test(p)) return new Response("Gone", { status: 410 }); return fetch(request); } }
Route it on www.witgoedaanbod.nl/* — requests containing an invalid percent-sequence get a 410 directly from Cloudflare, and everything else passes through to Railway untouched. Use 410 rather than 404: Google treats both as "not there", but 410 states permanent removal explicitly and tends to drop the URLs sooner.
If you would rather not run a Worker, a Bulk Redirect list mapping the nine exact URLs to their new slugs also works and gives you the 301 you actually wanted, at the cost of maintaining the list.
If you do not want Cloudflare in the picture
The no-infrastructure option is to manage the reporting side and wait: submit the nine URLs under Removals in Search Console to suppress them for six months, and confirm nothing on the site still links to them. This does not stop the 502 or the retries, it only quiets the report — but given the proportionate-impact point above, that is a defensible trade for nine dead URLs.
For the Railway team
The repro is already minimal. The useful detail is the asymmetry: the identical invalid sequence is forwarded fine in the query string and fails only in the path, which points at path parsing in railway-hikari rather than at request validation in general. Worth noting that a 400 would be a perfectly acceptable fix — the requirement is only that it not be 5xx, because that is what makes crawlers retry indefinitely.