README
¶
markdown-proxy
An HTTP proxy server for viewing Markdown files in a browser. Supports both local and remote access modes.

Motivation
Existing Markdown viewing environments have several pain points:
- Limited local preview: Tools like VS Code's Markdown Preview Enhanced are widely used for local Markdown viewing, but they can only display one preview pane at a time, making it difficult to reference multiple documents simultaneously.
- Incomplete diagram rendering on remote servers: Platforms such as GitHub and GitLab often do not render PlantUML diagrams embedded in Markdown files, leaving them displayed as raw code blocks.
markdown-proxy solves these problems by rendering Markdown files — including PlantUML diagrams — as HTML in a standard browser, where you can freely open multiple tabs and windows.
Comparison
How markdown-proxy compares to other Markdown viewing tools:
| Feature | markdown-proxy (this tool) | Markdown Preview Enhanced | grip | Madness |
|---|---|---|---|---|
| Type | HTTP server | VS Code extension | HTTP server | HTTP server |
| Local file viewing | ✅ | ✅ | ✅ | ✅ |
| Remote URL fetching | ✅ (GitHub/GitLab) | ❌ | ❌ | ❌ |
| Multi-tab viewing | ✅ | △ ¹ | ✅ | ✅ |
| Directory listing | ✅ | ❌ | ❌ | ✅ |
| Live reload | ✅ | ✅ | ✅ | ❌ |
| Mermaid diagrams | ✅ | ✅ | ❌ | ✅ |
| PlantUML diagrams | ✅ | ✅ | ❌ | ❌ |
| Math rendering | ✅ (KaTeX) | ✅ (KaTeX/MathJax) | ❌ | ❌ |
| Code highlighting | ✅ | ✅ | ✅ | ✅ |
| CSS themes | 3 built-in | 15+ built-in | GitHub only | Customizable |
| Full-text search | ❌ | ❌ | ❌ | ✅ |
| Export (PDF, HTML) | △ ² | ✅ (PDF, HTML, Word) | ✅ (HTML) | ❌ |
| Authentication | Token-based | — | — | HTTP Basic |
| Works offline | ✅ | ✅ | ❌ | ✅ |
| Runtime dependency | None (single binary) | VS Code | Python | Ruby |
¹ One preview pane per editor group ² Browser print-to-PDF via toolbar Print link
Features
- Render local and remote Markdown files as HTML
- Support for GFM (GitHub Flavored Markdown) with syntax highlighting
- Math rendering (
$...$for inline,$$...$$for display) via KaTeX - Code block rendering
- SVG: inline SVG rendering from
```svgcode blocks - Mermaid: client-side rendering via mermaid.js from
```mermaidcode blocks - PlantUML: server-side rendering from
```plantumlcode blocks (requires--plantuml-server)- Disabled by default because diagram content is sent to the specified server
- To use the public server:
--plantuml-server https://www.plantuml.com/plantuml
- SVG: inline SVG rendering from
- GitHub/GitLab integration
- Blob URL auto-conversion to raw URL (supports self-hosted GitLab with custom domains)
- Authentication via git credential helper (supports path-based credential matching)
- Redirect-based auth detection for self-hosted GitLab instances
- Toolbar actions
- Print: browser print with clean filename (toolbar hidden in print output)
- Source: link to original URL on remote server (remote pages only)
- Multiple CSS themes (GitHub, Simple, Dark) with switching UI
- Live reload for local files (auto-refreshes browser on file changes)
- Directory listing for local files
- Line anchor links:
[text](foo.md:12)or<a href="foo.md:12">links navigate to specific source lines with highlighting (Markdown and text files) - Text file rendering:
.txtfiles are displayed in HTML with line anchors, themes, and live reload - Link rewriting for seamless proxy navigation (including
file:///protocol conversion) - Top page with smart input (auto-detects file path or URL)
- Recently opened file history (localStorage)
- Two operation modes: local mode and remote mode
- Token-based authentication for remote access
- Access logging with automatic log rotation
- Single binary, no runtime dependencies
Quick Start
# Install
go install github.com/patakuti/markdown-proxy/cmd/markdown-proxy@latest
# Start the server
markdown-proxy
# Open in browser
open http://localhost:9080/
Enter a local file path (e.g., /path/to/README.md) or a remote URL (e.g., https://github.com/user/repo) on the top page.
Use Cases
- Reviewing multiple documents side by side: Open several Markdown files in separate browser tabs — no single-pane limitation like IDE preview plugins.
- Viewing PlantUML/Mermaid diagrams: Render diagrams embedded in Markdown that GitHub/GitLab don't display natively.
- Browsing private repositories: Access Markdown files from private GitHub/GitLab repos using your existing git credentials.
- Navigating RAG search reports: Reports generated by tools like Local Knowledge RAG MCP Server contain links to source documents. With IDE preview plugins (single-pane), switching between the report and referenced documents is tedious. In markdown-proxy, open the report in one tab and click through references in new tabs — navigate freely between them.
- Sharing a Markdown viewer with your team: Run in remote mode with token authentication to let team members view documentation through a browser.
URL Scheme
| Type | URL Format |
|---|---|
| Top page | http://localhost:9080/ |
| Local file | http://localhost:9080/local/path/to/file.md |
| Local directory | http://localhost:9080/local/path/to/dir/ |
| Remote (HTTP) | http://localhost:9080/http/server/path/to/file.md |
| Remote (HTTPS) | http://localhost:9080/https/server/path/to/file.md |
| GitHub repo | http://localhost:9080/https/github.com/user/repo/blob/main/README.md |
Line Anchor Links
Markdown and text files support line-level linking using the file:line syntax:
[text](foo.md:12)— links to line 12 offoo.md[text](foo.md:12-34)— links to lines 12–34 offoo.md[text](foo.txt:12)— links to line 12 offoo.txt<a href="foo.md:12">text</a>— same, using raw HTML
When navigating to a line anchor (#L12 or #L12-L34), the page scrolls to the target line and highlights the surrounding content. For text files, individual lines are highlighted; for Markdown files, the containing block element is highlighted. Highlighting is hidden in print output.
Usage
markdown-proxy [options]
Options
Note: Options use a single dash (e.g.,
-port) following Go'sflagpackage convention. Double dashes (--port) also work.
| Flag | Description | Default |
|---|---|---|
-port, -p |
Listen port | 9080 |
-listen |
Bind address (127.0.0.1 for local, 0.0.0.0 for remote) |
127.0.0.1 |
-theme |
Default CSS theme (github, simple, dark) |
github |
-plantuml-server |
PlantUML server URL | (disabled) |
-auth-token |
Authentication token (required in remote mode) | |
-auth-cookie-max-age |
Authentication cookie max age in days | 30 |
-access-log |
Access log file path | |
-access-log-max-size |
Max log file size in MB before rotation | 100 |
-access-log-max-backups |
Max number of old log files to retain | 3 |
-access-log-max-age |
Max days to retain old log files | 28 |
-verbose, -v |
Enable debug logging to stderr | false |
-version |
Show version and exit |
Operation Modes
Local Mode (default)
markdown-proxy
The server binds to 127.0.0.1 and all features are available:
- Local file access (
/local/...) - Remote file access (
/http/...,/https/...) - Live reload via SSE (
/_sse) - Private network access is allowed for remote file fetching
Remote Mode
markdown-proxy -listen 0.0.0.0 -auth-token my-secret-token
The server binds to the specified address for network access. For security:
- Local file access is disabled:
/local/and/_ssereturn 403 Forbidden - Authentication is required:
-auth-tokenmust be specified - Private network access is blocked: SSRF protection prevents fetching from internal IPs
- Top page shows URL input only (no local file path input)
Users must authenticate via a login page (/_login) by entering the access token. The token is stored in an HttpOnly cookie for the configured duration.
Access Logging
Access logs record each request in the following format:
2026-02-22T15:04:05+09:00 192.168.1.10 GET /https/github.com/user/repo 200 1234 150ms
-access-log /var/log/mdproxy/access.log: Log to a file with automatic rotation- In remote mode without
-access-log: Logs to stdout by default - In local mode without
-access-log: No access logging
Log rotation is handled automatically using configurable size, count, and age limits.
Live Reload
When viewing local Markdown files or directories (/local/...), the browser automatically reloads when the file or directory contents change. This uses Server-Sent Events (SSE) with filesystem notifications (fsnotify).
- Local files only: Remote files (
/http/...,/https/...) are not affected - Local mode only: Not available in remote mode
- No configuration needed: Works automatically for all local file views
- Debounced: Multiple rapid changes are coalesced into a single reload (100ms debounce)
Math Rendering
Mathematical expressions are rendered using KaTeX. Use standard LaTeX syntax:
- Inline math:
$E = mc^2$renders inline within text - Display math:
$$\int_0^\infty e^{-x} dx = 1$$renders as a centered block
No configuration needed. Math expressions are automatically detected and rendered.
Security
- Local mode: The server binds to
127.0.0.1only, accepting local connections only - Remote mode: Authentication is enforced via token. Local file access and private network fetching are automatically disabled
- SSRF protection: In remote mode, requests to private/internal IP addresses (e.g.,
10.x.x.x,192.168.x.x,127.x.x.x) are blocked. In local mode, private network access is allowed - DNS rebinding prevention: Resolved IP addresses are used directly for connections, preventing DNS rebinding attacks
- Constant-time token comparison: Authentication uses
crypto/subtle.ConstantTimeCompareto prevent timing attacks
Private Repository Access
markdown-proxy can access private repositories on GitHub and GitLab using your existing git credentials. When authentication is needed (e.g., 401, 403, or 404 from a private repo), markdown-proxy automatically invokes git credential fill to retrieve stored credentials.
If credentials are not configured, an error page with setup guidance is displayed instead of a raw error message.
GitHub
Option A: GitHub CLI (recommended)
gh auth login
This configures the git credential helper automatically.
Option B: Personal Access Token
- Create a token at https://github.com/settings/tokens (classic) or https://github.com/settings/tokens?type=beta (fine-grained)
- Required scope:
repo(classic) or repository read access (fine-grained) - Store it via git credential helper:
If no credential is returned, configure a credential helper (e.g.,echo -e "protocol=https\nhost=github.com\n" | git credential fillgit config --global credential.helper store) and save the token.
GitLab
gitlab.com:
glab auth login
Self-hosted GitLab:
glab auth login --hostname may not work on some self-hosted instances. If authentication fails, use a Personal Access Token (PAT) instead:
- Create a PAT at Settings > Access Tokens with
read_repositoryscope - Register it with glab:
glab auth login --hostname gitlab.example.com --token <YOUR_TOKEN>
This stores the PAT in git's credential helper, which markdown-proxy uses automatically.
Organization-specific Credentials
If you need different credentials for specific organizations, use path-based credential configuration:
[credential "https://github.com/my-org"]
helper = !gh auth git-credential
useHttpPath = true
Verifying Credentials
To verify that git can provide credentials for a host:
echo -e "protocol=https\nhost=github.com\n" | git credential fill
This should output username and password fields. If nothing is returned, credentials are not configured for that host.
Installation
Download
Download the latest binary from GitHub Releases.
go install
go install github.com/patakuti/markdown-proxy/cmd/markdown-proxy@latest
Build
make build
Cross-compile
# Linux
make linux
# Windows
make windows
Manual build
go build -o markdown-proxy ./cmd/markdown-proxy
Known Limitations
- Read-only viewer: No editing capabilities; this is a rendering-only tool.
- Limited file type support: Only
.md,.markdown, and.txtfiles are rendered as HTML. Other file types are served as-is. - PlantUML disabled by default: Diagram content is sent to an external server, so it requires explicit opt-in via
--plantuml-server. - GitHub/GitLab branch detection: When accessing a repository root URL, only
mainandmasterbranches are tried for README.md auto-detection. - No native PDF export: Use the toolbar's Print link to export via the browser's print-to-PDF feature. Page breaks are automatically avoided inside tables, code blocks, math expressions, images, blockquotes, and list items; headings are kept together with the following content.
- Hidden files excluded: Files and directories starting with
.are not shown in directory listings.
Contributing
Bug reports and feature requests are welcome via GitHub Issues. Pull requests are also appreciated — please open an issue first to discuss the change.
# Build and test locally
make build
Project Structure
cmd/markdown-proxy/ - Entry point
internal/
config/ - Command-line flag parsing, mode detection
server/ - HTTP server, routing, middleware (auth, access log)
handler/ - Request handlers (top, local, remote, SSE, login)
network/ - HTTP client with SSRF protection
markdown/ - Markdown→HTML conversion, link rewriting, code block processing
credential/ - git credential helper integration
github/ - GitHub/GitLab URL resolution
template/ - HTML templates and CSS themes