iamjjanga-ouo25 downloadsConvert redirect HTTPS to local redirect URL for better compatibility with external platforms.
Convert Obsidian's native obsidian:// URIs to HTTPS redirect URLs for better compatibility with external platforms like Slack, Notion, and web forums.

Many platforms (Slack, Notion, web forums) block or don't recognize Obsidian's custom obsidian:// URI scheme. This plugin solves that problem by generating standard HTTPS URLs that redirect back to your Obsidian vault.
How it works:
obsidian:// URI from your active fileExample transformation:
Input: obsidian://open?vault=my-vault&file=Project%2FMeeting
Output: https://go.obsidian-link.com/open?vault=my-vault&file=Project%2FMeeting
main.js, manifest.json, and styles.css from the latest releaseVaultFolder/.obsidian/plugins/obsidian-link/Cmd/Ctrl + P)https://go.obsidian-link.com)Important: The target URL server must be configured to redirect HTTPS requests back to obsidian:// URIs. See Backend Setup below.
The plugin generates HTTPS URLs, but you need a redirect service to make them work. Here are two ways to set up Cloudflare:
Use the Python automation script for fastest setup:
Prerequisites:
Setup Steps:
Get Cloudflare credentials:
Configure environment:
cd setup-cloudflare
cp .env.example .env
# Edit .env and add your CF_API_TOKEN and CF_ZONE_ID
Run setup script:
./run.sh
# Or manually:
uv sync
uv run python setup_cloudflare.py
The script will automatically:
go → your domain)https://go.your-domain.com/open?... → obsidian://open?...)For more control or if you prefer manual configuration:
go@ (your root domain) or your hosting providerRule name:
Obsidian Link Redirect
When incoming requests match:
(http.host eq "go.your-domain.com" and http.request.uri.path eq "/open")
Then:
Dynamicconcat("obsidian://open?", http.request.uri.query)
301 (Permanent Redirect)No (already in expression)Example:
Input: https://go.your-domain.com/open?vault=MyVault&file=Notes.md
Output: obsidian://open?vault=MyVault&file=Notes.md
3.1 Rate Limiting
Navigate to Security → Rate Limiting Rules → Create rule
Rule name: Obsidian Link Rate Limit
When incoming requests match:
(http.host eq "go.your-domain.com" and http.request.uri.path eq "/open")
Configuration:
Block60 per 1 minute60 secondsPer IP address429 Too Many Requests3.2 Query String Validation
Navigate to Security → WAF → Custom rules → Create rule
Rule 1: Block Long Query Strings
When: (http.host eq "go.your-domain.com" and
http.request.uri.path eq "/open" and
len(http.request.uri.query) gt 2048)
Then: Block (400 Bad Request)
Rule 2: Validate Required Parameters
When: (http.host eq "go.your-domain.com" and
http.request.uri.path eq "/open" and
(not http.request.uri.query contains "vault=" or
not http.request.uri.query contains "file="))
Then: Block (400 Bad Request)
Rule 3: Block Invalid Paths
When: (http.host eq "go.your-domain.com" and
not http.request.uri.path eq "/open")
Then: Block (404 Not Found)
Test normal redirect:
curl -I "https://go.your-domain.com/open?vault=Test&file=note.md"
# Expected: 301 redirect to obsidian://open?vault=Test&file=note.md
Test rate limiting:
for i in {1..65}; do
curl -I "https://go.your-domain.com/open?vault=Test&file=test.md"
echo "Request $i"
done
# First 60 should succeed, then get 429 Too Many Requests
Test validation rules:
# Missing parameter (should get 400)
curl -I "https://go.your-domain.com/open?vault=Test"
# Invalid path (should get 404)
curl -I "https://go.your-domain.com/invalid"
# Long query string (should get 400)
curl -I "https://go.your-domain.com/open?vault=Test&file=$(python3 -c 'print("A"*2500)')"
Enable Cloudflare Analytics to track:
Set up alerts for:
If you want to use a custom domain instead of go.obsidian-link.com:
https://go.your-domain.com)This plugin generates secure, shareable links with the following protections:
encodeURIComponent() to safely encode vault names and file pathsThe plugin uses encodeURIComponent() to encode vault names and file paths before generating URLs. This ensures special characters (<, >, ", ', /, etc.) are safely escaped, preventing malicious scripts from being injected into shared links.
Example:
File path: "Project/Meeting Notes <Draft>.md"
Encoded: "Project%2FMeeting%20Notes%20%3CDraft%3E.md"
1. Verify Link Sources
go.obsidian-link.com)2. Custom Target URL
3. Keep Obsidian Updated
obsidian:// URIs securely when up-to-dateIf you're hosting your own redirect service, implement these security measures:
Essential Security Checklist:
vault and file parameters/open endpoint, block all other pathsRecommended Rate Limit Settings:
| User Type | Requests/min | Status |
|---|---|---|
| Normal user | 5-10 | ✅ Allowed |
| Power user | 20-30 | ✅ Allowed |
| Automated scripts | 60+ | ⚠️ Blocked |
| Malicious attacks | Hundreds | ❌ Blocked |
Security Rule Priority:
For Cloudflare configurations, rules execute in this order:
Important: Because redirects execute before WAF rules, path traversal patterns (../) in query strings cannot be validated at the redirect layer. See Known Limitations below for details and mitigation strategies.
⚠️ Path Traversal Validation
Due to Cloudflare's execution order (Single Redirects run before security rules), the redirect service cannot validate dot-dot-slash (../) patterns in file paths. This means malicious links could potentially reference files outside the intended vault directory.
User Protection:
file parameter in URLs before clicking../ patterns in file pathsRisk Context:
For technical details, see Cloudflare Security Limitations.
A comprehensive security analysis is available in the project documentation:
If you discover a security vulnerability, please report it responsibly:
# Clone the repository
git clone https://github.com/iamjjanga-ouo/obsidian-link.git
cd obsidian-link
# Install dependencies
npm install
# Start development mode (watch for changes)
npm run dev
npm run devmain.js, manifest.json, and styles.css to your test vault's .obsidian/plugins/obsidian-link/ foldersrc/
├── main.ts # Plugin entry point, core logic
└── settings.ts # Settings interface and settings tab UI
manifest.json # Plugin metadata
esbuild.config.mjs # Build configuration
URL Transformation Logic:
[Target URL]/[Path]?[Query String]Obsidian API Usage:
this.app.workspace.getActiveFile() - Get current active filethis.app.vault.getName() - Get vault name for URI generationnavigator.clipboard.writeText() - Copy to clipboardnew Notice() - Show toast notificationsthis.addCommand() - Register command palette commands# Run ESLint
npm run lint
# Build for production
npm run build
minAppVersion in manifest.json if needednpm version patch|minor|majormanifest.json, package.json, and versions.jsonnpm run buildmanifest.json, main.js, styles.css as release assets"No active file found" error:
Special characters in file names:
encodeURIComponent()Redirect not working:
Possible causes:
Solutions:
curl -I to see actual HTTP responseLink doesn't open Obsidian:
301 or 302 status codeLocation header contains obsidian:// protocolobsidian:// URIsNormal users hitting rate limit:
Cause: Rate limit setting too restrictive (60 req/min may be too low for some workflows)
Solutions:
DNS record already exists:
Script fails with API error:
.env file is properly formatted (no spaces around =)Environment variables not loading:
.env file exists in setup-cloudflare/ directorypython-dotenv is installed (uv sync)export CF_API_TOKEN=...Contributions are welcome! Please feel free to submit a Pull Request.
Cloudflare Documentation:
Security Resources:
Obsidian Development:
This project is licensed under the MIT License.
Developed by iamjjanga
For more information about the Obsidian Plugin API, see https://docs.obsidian.md