Why downloads break on English pages: routes versus static files
A subtle failure on this site affected otherwise valid attachments: a link worked from a Chinese article but acquired an /en/ prefix from its English version. The files lived under the shared /downloads/evidence/ directory; no separate English attachment existed. The label looked correct, but the routing layer treated a file as a localized page.
The walkthrough follows the fix at revision 1a6b177. You need browser developer tools and a basic distinction between source files, build output, and deployment. The goal is not merely an HTTP 200: both languages should retrieve the correct shared file, while an HTML utility should still open as a usable page.
Classify the destination before localizing it
| Destination | URL from an English page | Behavior |
|---|---|---|
| Article | /en/article/react-chinese-ime-onchange | Open the English article |
| Shared ZIP | /downloads/evidence/glb-measurements.zip | Keep shared path; offer download |
| Shared Markdown | /downloads/evidence/react-ime-2026-07.md | Keep shared path; offer download |
| HTML utility | /downloads/evidence/ime-event-logger.html | Keep shared path; open in browser |
Two decisions are independent: whether to localize the URL and whether to request a download. A shared HTML utility needs no /en/ prefix, but forcing every file under downloads to download would prevent its intended in-browser use. A vague attachment flag can hide this distinction and turn a ZIP fix into a utility regression.
A native anchor can still have the wrong URL
LocalizedLink converts internal paths to the active language. Even if a navigation performs a full document load, an already rewritten /en/downloads/... href remains wrong. Switching to a document reload is insufficient; the shared asset must bypass page-path localization as well.
// Reduced from the site's link-block handling.
const isDownload = href.endsWith('.zip') || href.endsWith('.md');
const isSharedFile = href.startsWith('/downloads/');
if (isSharedFile || href.endsWith('.zip')) {
return <a href={href} download={isDownload}>{label}</a>;
}
return <LocalizedLink to={href}>{label}</LocalizedLink>;This is a reduced form of the inspected implementation, not a universal URL classifier. It covers the site’s shared /downloads/ paths and ZIP links. A URL ending in .zip?revision=2 does not match endsWith(".zip"). Under /downloads/, it keeps the correct path, but this version does not infer the download attribute from that suffix. Enumerate supported URL forms before extending the rule.
For query strings, mixed-case suffixes, or more file types, parse a pathname for classification while preserving the original href, query, and fragment. Cross-origin links need separate treatment. The download attribute is not an unconditional command for arbitrary remote files: origin restrictions, response headers, and browser settings affect the outcome.
A prerendered site has two link outputs to inspect
The site renders articles through React and through a build-time HTML renderer. Fixing only React can leave an incorrect href in the first response. Fixing only HTML can let React restore the wrong URL after startup. Both renderers must preserve shared paths and agree on download behavior.
Compare the href in View Source or the raw response with the href in Elements after startup. If they disagree, resolve the renderer mismatch before blaming caching. Then click from the English article itself. Pasting the correct asset URL into the address bar proves the file exists, not that the article points to it.
Open the input-event logger used in this case (HTML)Download the IME verification notes used in this case (Markdown)HTTP 200 is not proof that the file arrived
An SPA host can fall back to the application HTML for an unknown path. A status-only check may therefore report success for a broken attachment. Inspect the content type and body as well: a ZIP should not begin with an HTML doctype, and a Markdown download should not contain the website’s navigation shell. For known assets, compare the deployed bytes with the local source using SHA-256.
curl --fail --location \
https://keeponfirst.com/downloads/evidence/glb-measurements.zip \
--output /tmp/glb-measurements.zip
file /tmp/glb-measurements.zip
unzip -t /tmp/glb-measurements.zip
shasum -a 256 public/downloads/evidence/glb-measurements.zip /tmp/glb-measurements.zipRun the final command from a checkout containing the source asset. Matching digests establish byte equality for that download. Without a local copy, at least inspect archive validity, file names, and the README against the article’s description. Matching hashes verify the transferred file, not every technical conclusion made inside it.
A small, practical acceptance matrix
| Scenario | Expected check |
|---|---|
| Click ZIP from both language versions | Shared URL; valid archive |
| Click Markdown | Text file, not the SPA shell |
| Click the HTML utility | Usable page, not a forced download |
| Compare initial HTML and mounted DOM | Matching href and download semantics |
| Request a nonexistent asset | Recognize a 404 or fallback rather than accepting it |
| Add a query string | Define the expected behavior separately |
A routing-system rewrite is not the first step here. Define the page-versus-file boundary, align both outputs, and verify from the reader’s entry point. When an attachment contains the evidence or runnable example behind an article, reliable access to it is part of delivering the article itself.