Astro, fix .md in links
By Flavio Copes
How to fix Markdown links ending in .md breaking in Astro, using a rehype plugin so internal Obsidian-style links resolve to the right page URLs.
If your Markdown links end in .md, they break when Astro builds the site: pages have clean URLs, so a link pointing to a .md file leads to a page that doesn’t exist. The fix is a rehype plugin that rewrites those links at build time.
Here’s how I got into this. Using Obsidian to edit my Markdown files, I’ve started to link lessons using Obsidian’s own internal linking.
It’s great because with Obsidian link tracking I can move a file around, change folder or filename, and links don’t break.
Links however use a .md extension, since we’re linking markdown files here:
[3-absolute-positioning](../css-positioning/3-absolute-positioning.md)
Why do these links break?
Obsidian links files on disk. Astro takes those files and turns them into pages, and pages have URLs without the extension: the file 3-absolute-positioning.md becomes a URL like /css-positioning/3-absolute-positioning/.
But Astro doesn’t rewrite the links inside your Markdown content. The href stays exactly as you wrote it, .md included. In Obsidian the link works. In the browser, it’s a 404.
That is still true on Astro 7. The compiler ships pages with clean URLs. It does not strip .md from links in your Markdown for you.
The fix
I found this rehype plugin for Astro: https://github.com/vernak2539/astro-rehype-relative-markdown-links
rehype plugins run after your Markdown is converted to HTML, and they can transform that HTML before Astro writes the page. This one finds links pointing to .md files and rewrites them to the corresponding page URLs.
It fixes this exact problem. Current releases declare peer support for Astro 2 through Astro 7 (>=2 <8), so on Astro 7 you can install it from npm:
npm install astro-rehype-relative-markdown-links
Then add it in your Astro config, in the markdown.rehypePlugins array:
import { defineConfig } from 'astro/config'
import rehypeMarkdownLinks from 'astro-rehype-relative-markdown-links'
export default defineConfig({
markdown: {
rehypePlugins: [rehypeMarkdownLinks]
}
})
If you prefer a vendored copy, you can still drop the plugin file next to the project and import the local path instead. Same config shape.
One thing I had to adjust
Only thing I noticed is, maybe because I use content collections, my content collection folder name was prepended to the URLs the plugin generated. The link resolved to the file path, not to the final page URL.
So I hardcoded a little custom fix in the code that only works in my use case, but well, job done.
Since version 0.17.0 the plugin can map a collection to a page path that doesn’t match the folder name, through its options. Check the plugin docs before patching anything.
If you hit the same problem, compare the URL the plugin generates with the actual URL of the page. The difference tells you exactly what to strip.
Want me to talk about your product? You can sponsor this site.
Related posts about astro: